Типы прерываний и источники возникновения запросов. Принципы обработки прерываний и флаговая логика микропроцессоров , страница 8
3.4.2. Программа работы
В процессе подготовки к выполнению работы написать и отладить следующие процедуры:
· вывода на экран строки символов прямым отображением в память, так как при использовании функций DOS или BIOS аппаратные прерывания, возникающие в случайные моменты времени, могут привести к разрушению операционной системы [13],
· преобразования байта информации, хранящегося в ОЗУ, в десятичный эквивалент в символьном виде.
Выполните следующие эксперименты по перехвату прерываний:
· Перехватите прерывание int 8h, предусмотрев в программе обработки передачу управления исходному обработчику. Для иллюстрации результата выведите в левом верхнем углу дисплея звездочку прямым отображением в память.
· Повторите эксперимент без передачи управления. Не забудьте снять заявку в регистре ISR контроллера прерываний.
· Замените в обработчике прямое отображение в видеопамять на процедуру инкрементации счетчика прерываний от таймера так, чтобы по истечении одной секунды происходил вызов пользовательского прерывания int 060h. Перед возвратом из обработчика int 8 передайте управление исходному обработчику, чтобы не сбивать системные часы. Для иллюстрации результата в обработчик int 60h включите вывод на экран символа.
· Убедившись, что перехватывание аппаратного прерывания и вновь созданное собственное прерывание происходят успешно, замените процедуру вывода в int 60h на подсчет относительного времени с момента начала работы программы.
· Напишите программу вывода на экран времени работы вашей программы. Для этого в основную программу включите цикл, ограничивающий время работы. Функции коррекции времени удобнее выполнять в пользовательском прерывании int 60h, а функции преобразования в символьный вид и вывод на экран — в теле основной программы. Окончание цикла можно установить по значению относительного времени работы программы. Возможный алгоритм решения этой задачи приведен на рис. 3.9, 3.10
Рис. 3.9. Рекомендуемый алгоритм основной программы вывода времени работы программы
Рис. 3.10. Рекомендуемый алгоритм счета времени
· Используя свою программу, замаскируйте доступ к клавиатуре на 1 мин. Попробуйте «перезапустить» компьютер с «теплого старта».
· В основной программе замените функцию 25h прерывания DOS 21h установки вектора прерывания. Позаботьтесь о том, чтобы во время смены вектора аппаратного прерывания не произошло другое аппаратное прерывание.
· Исключите цикл, ограничивающий время работы вашей программы. Реализуйте возможность выхода из программы по «горячим клавишам». Для этого перехватите аппаратное прерывание от клавиатуры.
· Преобразуйте программу так, чтобы на экран выводились часы суточного времени. Для этого в начале основной программы примените вызов функции 2CH прерывания DOS 21h.
· Выполните индивидуальное задание, полученное у преподавателя.
3.4.3. Методические указания к работе
Режим прерываний в общей системе обмена информацией с периферийными устройствами
Обмен с ПУ может осуществляться двумя способами: по прерываниям и по прямому доступу к памяти (ПДП). Как правило, ПДП
применяется для поблочной передачи информации между ОЗУ и ВЗУ.
Устройство, принимающее на себя функции управления процессом обмена — контроллер ПДП. Прямой доступ позволяет на время обмена разгрузить процессор, освободив его от управления передачей, и существенно повысить как скорость обмена, так и быстродействие процесса в целом.
ПУ, отличные от ВЗУ побайтно обмениваются либо с процессором, либо с памятью (ОЗУ). Очевидно, что целесообразно применять единый механизм обмена и в том, и в другом случае во избежание излишних аппаратных затрат и для унификации программного обеспечения. Побайтный обмен позволяет обеспечить гибкость при распределении ресурсов системы с целью повышения быстродействия
MS-DOS для программиста
5.3. Установка обработчиков прерываний
Последнее, что делает функция tsrinit перед возвращением управления — встраивание обработчиков некоторых прерываний. При этом для обеспечения возможности выгрузки резидентной программы из памяти сохраняются старые значения изменяемых векторов прерываний.
Перед тем как приступить к изменению векторов прерываний, функция tsrinit запрещает аппаратные прерывания, вызывая функцию _disable . После того как изменение векторов прерываний завершено, вызывается функция _enable , разрешающая обработку аппаратных прерываний:
Обработчики всех прерываний, за исключением прерывания INT 13h, составлены на языке С. Так как прерывание INT 13h возвращает результат выполнения операций в регистре флагов, соответствующий обработчик удобнее составить на языке ассемблера.
Расскажем подробно о том, какие вектора прерываний мы переопределяем и зачем.
Прерывание INT 1Ch
Прерывание INT 1Ch является программным и вызывается обработчиком аппаратного прерывания от таймера INT 08h приблизительно 18,2 раза в секунду.
Наша программа использует это прерывание, для того чтобы выводить в правом верхнем углу экрана мигающие символы «*» и «+». Такое мигание нужно только для индикации активности резидентной программы. Если индикация не требуется, вы можете не изменять соответствующий вектор прерывания и не предусматривать обработчик для этого прерывания.
Наш обработчик прерывания INT 1Ch называется new_int1c и имеет следующий вид:
Массив screen содержит дальние указатели на массив целых чисел, определенные следующим образом:
В первый элемент этого массива мы записываем указатель на область видеопамяти , соответствующую самой верхней строке экрана. Для цветного видеоконтроллера этот адрес равен B800h:0000h.
Для того чтобы символы не мигали слишком часто, обработчик прерывания INT 1Ch подсчитывает прерывания в статическом счетчике count. Если значение этого счетчика больше 2, выводится символ ‘*», если меньше — символ «+».
Перед тем как возвратить управление, функция new_int1c вызывает старый обработчик прерывания INT 1Ch , адрес которого хранится в переменной old_int1c. Для этого используется функция _chain_intr .
Прерывание INT 2Fh
Обработчик прерывания INT 2Fh , который встраивает наша программа, выполняет две функции.
Во-первых, этот обработчик используется для предотвращения повторной загрузки резидентной программы в память.
Во-вторых, он нужен для выгрузки резидентной программы из памяти.
Исходный текст обработчика прерывания INT 2Fh представлен ниже:
Вызов прерывания INT 2Fh происходит при запуске нашей программы, а также и при запуске других резидентных программ. Регистр AH при этом должен содержать идентификатор резидентной программы. Для программы TSRDEMO мы выбрали идентификатор FFh, хотя никто не сможет гарантировать, что такой идентификатор уже не используется другой программой. Для надежности вы можете указать дополнительный идентификатор, например, в виде текстовой строки, адрес которой передается через другие регистры. Мы не стали этого делать для сокращения объема листинга программы.
Итак, если при вызове прерывания INT 2Fh регистр AH содержит значение FFh, наш обработчик прерывания считает, что вызов выполняет программа TSRDEMO.
В этом случае он дополнительно проверяет содержимое регистра AL, в котором находится код выполняемой функции.
Если этот код равен 0, прерывание INT 2Fh вызвано для проверки наличия программы TSRDEMO в памяти. В ответ обработчик возвращает в регистре AX значение 00FFh. Это и есть признак того, что программа TSRDEMO уже загружена в память и ее повторная загрузка невозможна.
Если же код равен 1, программа TSRDEMO была запущена с параметром u для выгрузки своей копии из памяти.
В этом случае обработчик прерывания INT 2Fh сохраняет в глобальной переменной ExitAddress адрес завершения, переданный обработчику в регистрах BX:DX. Затем проверяется флаг tsr_already_active, который установлен, если резидентная программа активна и ее в данный момент нельзя выгружать из памяти.
Далее обработчик разрешает аппаратные прерывания, которые были запрещены перед вызовом прерывания INT 2Fh для выгрузки, и предпринимает попытку выгрузить резидентную программу, вызывая функцию tsr_exit.
Заметим, что после выгрузки резидентной программы функция tsr_exit передает управление MS-DOS, а не возвращает его обратно в программу.
Попытка выгрузки программы может окончиться неудачно, если пользователь загрузил после программы TSRDEMO другую резидентную программу, изменившую вектора прерываний, для которых программа TSRDEMO установила свои обработчики. В этом случае наша программа «не знает», как восстановить вектора прерываний и не выгружается из памяти. Тем не менее, она блокирует свою работу, записывая в переменную tsr_already_active значение -FFFFh.
Подробнее процесс выгрузки резидентной программы будет рассмотрен позже.
Заметим, что если содержимое регистра AX при вызове прерывания INT 2Fh не равно FF00h или FF01h, наш обработчик прерывания передает управление по цепочке, вызывая для этого функцию _chain_intr .
Прерывание INT 09h
Аппаратное прерывание от клавиатуры INT 09h обрабатывается для того чтобы обнаружить комбинацию клавиш, предназначенную для активизации резидентной программы:
Когда программа уже активна, установлен флаг tsr_already_active. В этом случае наш обработчик прерывания передает управление по цепочке, вызывая старый обработчик.
Если программа TSRDEMO не активна, наш обработчик прерывания вводит код нажатой клавиши из порта клавиатуры и проверяет, была ли нажата клавиша . Затем проверяется код нажатой клавиши. В том случае, если пользователь нажал комбинацию клавиш , нужно активизировать резидентную программу.
Однако активизация во время обработки аппаратного прерывания от клавиатуры — не самое лучшее решение. Поэтому наш обработчик просто устанавливает флаг popup_while_dos_busy, который служит запросом на активизацию.
Активизация резидентной программы будет выполнена позднее, при обработке аппаратного прерывания INT 08h или прерывания INT 28h .
Для корректной обработки аппаратного прерывания необходимо выдать в контроллер прерывания соответствующую команду, разрешающую обработку других прерываний. Эту задачу решают несколько команд, оформленных как asm-вставки в исходный текст обработчика.
Прерывание INT 08h
Активизация резидентной программы TSRDEMO не выполняется сразу после того, как пользователь нажал комбинацию клавиш . Для активизации необходимо выбрать подходящий момент, когда прерываемая программа не вызывает функцию MS-DOS или прерывание BIOS , предназначенное для работы с диском.
Наша программа использует несколько возможностей для своей активизации. В частности, обработчик аппаратного прерывания таймера INT 08h периодически проверяет возможность активизации:
Прежде всего, проверяется флаг tsr_already_active. Этот флаг устанавливается, когда резидентная программа уже активизирована. Проверка флага активизации позволяет избежать повторной активизации, которая может привести к катастрофе.
Активизация не выполняется также и в том случае, если установлен флаг InDos или unsafe_flag (последний устанавливается, если вызван обработчик прерывания INT 13h ).
Флаг popup_while_dos_busy установлен в том случае, когда был запрос на активизацию. Он устанавливается обработчиком аппаратного прерывания от клавиатуры INT 08h . Если запроса на активизацию нет, этот флаг не установлен и, следовательно, активизацию выполнять не нужно.
Если активизация возможна, сбрасываются флаги popup_while_dos_busy и устанавливается флаг tsr_already_active. Затем вызывается старый обработчик прерывания INT 08h и разрешаются прерывания.
Далее вызывается функция activate_tsr, которая выполняет все действия, необходимые для активизации. Она будет описана отдельно. После возвращения управления из этой функции флаг tsr_already_active сбрасывается.
В том случае, когда активизация резидентной программы невозможна, наш обработчик прерывания INT 08h вызывает старый обработчик и возвращает управление прерванной программе.
Прерывание INT 28h
Прерывание INT 28h вызывается MS-DOS, когда она ожидает ввод данных от клавиатуры или, иными словами, ничем особенным не занята. Поэтому обработчик прерывания INT 28h может активизировать резидентную программу, если на это есть запрос от пользователя.
Приведем исходный текст обработчика прерывания INT 28h :
Обработчик прерывания INT 28h содержит счетчик рекурсивных вызовов int_28_in_progress, который анализируется при активизации резидентной программы функцией activate_tsr.
Если есть запрос на активизацию, проверяются флаги tsr_already_active и unsafe_flag. Дополнительно проверяется, не выполняется ли попытка активизировать резидентную программу во время обработки прерывания MS-DOS, отличного от INT 28h . Для этого вызывается функция Int28DosBusy.
Обратите внимание на различие в способе проверки возможности активизации при обработке прерывания INT 08h и INT 28h .
В первом случае вызывается функция DosBusy:
Эта функция проверяет содержимое флагов InDos и флага обработки критической ошибки. Если MS-DOS не выполняет обработку критической ошибки и если прерываемая программа не вызывает функцию MS-DOS, можно выполнять активизацию. В этом случае функция DosBusy возвращает значение 0.
Однако при вызове прерывания INT 28h флаг InDos установлен всегда, так как указанное прерывание — это тоже прерывание MS-DOS. В данном случае для проверки возможности активизации используется другая функция, которая называется Int28DosBusy.
Эта функция допускает однократный (не рекурсивный) вызов функции INT 28h , проверяя значение, которое записывается в байт памяти, отведенный для флага InDos:
При рекурсивных вызовах функций MS-DOS значение, хранящееся в этом байте, увеличивается на 1. Активизация резидентной программы допускается только в том случае, когда в этом байте находится значение 1, т. е. когда нет рекурсии.
Обработчик прерывания INT 28h активизирует резидентную программу точно таким же способом, что и обработчик прерывания INT 08h , а именно, вызывая специально предназначенную для активизации функцию activate_tsr, определенную в нашей программе.
Прерывание INT 13h
Обработчик прерывания INT 13h составлен на языке ассемблера. Его единственное назначение — увеличение значения флага unsafe_flag при каждом вызове прерывания INT 13h и уменьшение при возврате из этого прерывания. Когда программа TSRDEMO будет предпринимать попытку активизации во время обработки прерывания INT 13h, она проверит значение флага unsafe_flag. Если оно не будет равно 0, активизация невозможна, так как в данный момент времени выполняется обработка прерывания INT 13h.
Исходный текст обработчика прерывания INT 13h представлен ниже:
Для обеспечения возможности адресации глобальной переменной unsafe_flag регистр DS устанавливается на сегмент данных программы TSRDEMO.
Адрес старого обработчика прерывания INT 13h находится в переменной old_int13, куда он записывается функцией get_int_13. Эта функция вызывается из функции tsrinit. Ее исходный текст вы найдете в листинге 5.3 (см. ниже).
Перехват прерываний
Перехват прерываний в работе процессора во время выполнении текущей программы. Типы и особенности развития данных ситуаций. Практические примеры обработчика программного прерывания. Определение значения нескольких векторов, установление нового значения.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | контрольная работа |
Язык | русский |
Дата добавления | 13.08.2011 |
Размер файла | 81,9 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Размещено на http://www.allbest.ru/
1. Перехват прерываний
В архитектуре процессоров 80×86 предусмотрены особые ситуации, когда процессор прекращает (прерывает) выполнение текущей программы и немедленно передает управление программе-обработчику, специально написанной для обработки этой конкретной ситуации. Такие особые ситуации делятся на два типа: прерывания и исключения, в зависимости от того, вызвало ли эту ситуацию какое-нибудь внешнее устройство или выполняемая процессором команда. Исключения делятся далее на три типа: ошибки, ловушки и остановы, в зависимости от того, когда по отношению к вызвавшей их команде они происходят. Ошибки происходят перед выполнением команды, так что обработчик такого исключения получит в качестве адреса возврата адрес ошибочной команды (начиная с процессоров 80286), ловушки происходят сразу после выполнения команды, так что обработчик получает в качестве адреса возврата адрес следующей команды, и наконец, остановы могут происходить в любой момент и вообще не предусматривать средств возврата управления в программу.
Команда INT (а также INTO и INT3) используется в программах как раз для того, чтобы вызывать обработчики прерываний (или исключений). Фактически они являются исключениями ловушки, поскольку адрес возврата, который передается обработчику, указывает на следующую команду, но так как эти команды были введены до разделения особых ситуаций на прерывания и исключения, их практически всегда называют командами вызова прерываний. Ввиду того, что обработчики прерываний и исключений в DOS обычно не различают механизм вызова, с помощью команды INT можно передавать управление как на обработчики прерываний, так и исключений.
Когда в реальном режиме выполняется команда INT, управление передается по адресу, который считывается из специального массива, таблицы векторов прерываний, начинающегося в памяти по адресу 0000h:0000h. Каждый элемент этого массива представляет собой дальний адрес обработчика прерывания в формате сегмент: смещение или 4 нулевых байта, если обработчик не установлен. Команда INT помещает в стек регистр флагов и дальний адрес возврата, поэтому, чтобы завершить обработчик, надо выполнить команды popf и retf или одну команду iret, которая в реальном режиме полностью им аналогична.
; Пример обработчика программного прерывания
int_handler proc far
После того как обработчик написан, следующий шаг — привязка его к выбранному номеру прерывания. Это можно сделать, прямо записав его адрес в таблицу векторов прерываний, например так:
push 0; сегментный адрес таблицы
pushf; поместить регистр флагов в стек
cli; запретить прерывания
; (чтобы не произошло аппаратного прерывания между следующими
; командами, обработчик которого теоретически может вызвать INT 87h
; в тот момент, когда смещение уже будет записано, а сегментный
; адрес еще нет, что приведет к передаче управления
; в неопределенную область памяти)
; поместить дальний адрес обработчика int_handler в таблицу
; векторов прерываний, в элемент номер 87h (одно из неиспользуемых прерываний)
mov word ptr es: [87h*4], offset int_handler
mov word ptr es: [87h*4+2], seg int_handler
popf; восстановить исходное значение флага IF
Теперь команда INT 87h будет вызывать наш обработчик, то есть приводить к записи 0 в регистр АХ.
Перед завершением работы программа должна восстанавливать все старые обработчики прерываний, даже если это были неиспользуемые прерывания типа 87h — автор какой-нибудь другой программы мог подумать точно так же. Для этого надо перед предыдущим фрагментом кода сохранить адрес старого обработчика, так что полный набор действий для программы, перехватывающей прерывание 87h, будет выглядеть следующим образом:
; скопировать адрес предыдущего обработчика в переменную old_handler
mov eax, dword ptr es: [87h*4]
mov dword ptr old_handler, eax
; установить наш обработчик
mov word ptr es: [87h*4], offset int_handler
mov word ptr es: [87h*4+2], seg int_handler
; восстановить предыдущий обработчик
mov eax, word ptr old_handler
mov word ptr es: [87h*4], eax
Хотя прямое изменение таблицы векторов прерываний и кажется достаточно удобным, все-таки это не лучший подход к установке обработчика прерывания, и пользоваться им следует только в случаях крайней необходимости, например внутри обработчиков прерываний. Для обычных программ DOS предоставляет две системные функции: 25h и 35h — установить и считать адрес обработчика прерывания, которые и рекомендуются к использованию в обычных условиях:
; скопировать адрес предыдущего обработчика в переменную old_handler
mov ax, 3587h; АН = 35h, AL = номер прерывания
int 21h; функция DOS: считать
; адрес обработчика прерывания
mov word ptr old_handler, bx; возвратить
mov word ptr old_handler+2, es; и сегментный
; установить наш обработчик
mov ax, 2587h; АН = 25h, AL = номер прерывания
mov dx, seg int_handler; сегментный адрес
mov dx, offset int_handler; смещение в DX
int 21h; функция DOS: установить
; (не забывайте, что ES изменился после вызова функции 35h!)
; восстановить предыдущий обработчик
lds dx, old_handler; сегментный адрес в DS и смещение в DX
mov ax, 2587h; АН = 25h, AL = номер прерывания
int 21h; установить обработчик
Обычно обработчики прерываний используют для того, чтобы обрабатывать прерывания от внешних устройств или чтобы обслуживать запросы других программ. Эти возможности рассмотрены далее, а здесь показано, как можно использовать обычный обработчик прерывания (или, в данном случае, исключения ошибки) для того, чтобы быстро найти минимум и максимум в большом массиве данных.
; находит минимальное и максимальное значения в массиве слов
; Ввод: DS:BX = адрес начала массива
; СХ = число элементов в массиве
; АХ = максимальный элемент
ВХ = минимальный элемент
minmax proc near
; установить наш обработчик прерывания 5
mov еах, dword ptr es: [5*4]
mov dword ptr old_int5, eax
mov word ptr es: [5*4], offset int5_handler
mov word ptr es: [5*4]+2, cs
; инициализировать минимум и максимум первым элементом массива
mov ax, word ptr [bx]
mov word ptr lower_bound, ax
mov word ptr upper_bound, ax
mov di, 2; начать со второго элемента
mov ax, word ptr [bx] [di]; считать элемент в АХ
bound ax, bounds; команда BOUND вызывает
; исключение — ошибку 5,
; если АХ не находится в пределах lower_bound/upper_bound
add di, 2; следующий элемент
loop bcheck; цикл на все элементы
; восстановить предыдущий обработчик
mov eax, dword ptr old_int5
mov dword ptr es: [5*4], eax
mov ax, word ptr upper_bound
mov bx, word ptr lower_bound
; обработчик IT 5 для процедуры minmax
; сравнить АХ со значениями upper_bound и lower_bound и копировать
; AX в один из них, обработчик не обрабатывает конфликт между
; исключением BOUND и программным прерыванием распечатки экрана INT 5.
; Нажатие клавиши PrtScr в момент работы процедуры minmax приведет
; к ошибке. Чтобы это исправить, можно, например, проверять байт,
; на который указывает адрес возврата, если это CDh
; (код команды INT), то обработчик был вызван как INT 5
int5_handler proc far
cmp ax, word ptr lower_bound; сравнить АХ с нижней границей,
jl its_lower; если не меньше —
; это было нарушение
mov word ptr upper_bound, ax; верхней границы
mov word ptr lower_bound, ax; если это было нарушение
iret; нижней границы
Прерывания от внешних устройств, или аппаратные прерывания — это то, что понимается под термином «прерывание». Внешние устройства (клавиатура, дисковод, таймер, звуковая карта и т.д.) подают сигнал, по которому процессор прерывает выполнение программы и передает управление на обработчик прерывания. Всего на персональных компьютерах используется 15 аппаратных прерываний, хотя теоретически возможности архитектуры позволяют довести их число до 64.
Рассмотрим их кратко в порядке убывания приоритетов (прерывание имеет более высокий приоритет, и это означает, что, пока не завершился его обработчик, прерывания с низкими приоритетами будут ждать своей очереди).
IRQ0 (INT — прерывание системного таймера. Это прерывание вызывается 18,2 раза в секунду. Стандартный обработчик этого прерывания вызывает INT 1Ch при каждом вызове, так что, если программе необходимо только регулярно получать управление, а не перепрограммировать таймер, рекомендуется использовать прерывание 1Ch.
IRQ1 (INT 9) — прерывание клавиатуры. Это прерывание вызывается при каждом нажатии и отпускании клавиши на клавиатуре. Стандартный обработчик этого прерывания выполняет довольно много функций, начиная с перезагрузки по Ctrl-Alt-Del и заканчивая помещением кода клавиши в буфер клавиатуры BIOS.
IRQ2 — к этому входу на первом контроллере прерываний подключены аппаратные прерывания IRQ8 — IRQ15, но многие BIOS перенаправляют IRQ9 на INT 0Ah.
IRQ8 (INT 70h) — прерывание часов реального времени. Это прерывание вызывается часами реального времени при срабатывании будильника и если они установлены на генерацию периодического прерывания (в последнем случае IRQ8 вызывается 1024 раза в секунду).
IRQ9 (INT 0Ah или INT 71h) — прерывание обратного хода луча. Вызывается некоторыми видеоадаптерами при обратном ходе луча. Часто используется дополнительными устройствами (например, звуковыми картами, SCSI-адаптерами и т.д.).
IRQ10 (INT 72h) — используется дополнительными устройствами.
IRQ11 (INT 73h) — используется дополнительными устройствами.
IRQ12 (INT 74h) — мышь на системах PS используется дополнительными устройствами.
IRQ13 (INT 02h или INT 75h) — ошибка математического сопроцессора. По умолчанию это прерывание отключено как на FPU, так и на контроллере прерываний.
IRQ14 (INT 76h) — прерывание первого IDE-контроллера «операция завершена».
IRQ15 (INT 77h) — прерывание второго IDE-контроллера «операция завершена».
IRQ3 (INT 0Bh) — прерывание последовательного порта COM2 вызывается, если порт COM2 получил данные.
IRQ4 (INT 0Ch) — прерывание последовательного порта СОМ1 вызывается, если порт СОМ1 получил данные.
IRQ5 (INT 0Dh) — прерывание LPT2 используется дополнительными устройствами.
IRQ6 (INT 0Eh) — прерывание дисковода «операция завершена».
IRQ7 (INT 0Fh) — прерывание LPT1 используется дополнительными устройствами.
Самые полезные для программ аппаратные прерывания — прерывания системного таймера и клавиатуры. Так как их стандартные обработчики выполняют множество функций, от которых зависит работа системы, их нельзя заменять полностью, как мы делали это с обработчиком INT 5. Следует обязательно вызвать предыдущий обработчик, передав ему управление следующим образом (если его адрес сохранен в переменной old_handler, как в предыдущих примерах):
Эти две команды выполняют действие, аналогичное команде INT (сохранить флаги в стеке и передать управление подобно команде call), так что, когда обработчик завершится командой IRET, управление вернется в нашу программу. Так удобно вызывать предыдущий обработчик в начале собственного. Другой способ — простая команда jmp:
приводит к тому, что, когда старый обработчик выполнит команду IRET, управление сразу же перейдет к прерванной программе. Этот способ применяют, если нужно, чтобы сначала отработал новый обработчик, а потом он передал бы управление старому.
Посмотрим, как работает перехват прерывания от таймера на следующем примере:
; демонстрация перехвата прерывания системного таймера: вывод текущего времени
; в левом углу экрана
186; для pusha/popa и сдвигов
start proc near
; сохранить адрес предыдущего обработчика прерывания 1Ch
mov ax, 351Ch; АН = 35h, AL = номер прерывания
int 21h; функция DOS: определить адрес обработчика
mov word ptr old_int1Ch, bx; прерывания
mov word ptr old_int1Ch+2, es; (возвращается в ES:BX)
; установить наш обработчик
mov ax, 251Ch; АН = 25h, AL = номер прерывания
mov dx, offset int1Ch_handler; DS:DX — адрес обработчика
int 21h; установить обработчик прерывания 1Ch
; здесь размещается собственно программа, например вызов command.com
int 21h; ожидание нажатия на любую клавишу
; восстановить предыдущий обработчик прерывания 1Ch
mov ax, 251Ch; АН = 25h, AL = номер прерывания
mov dx, word ptr old_int1Ch+2
mov dx, word ptr cs:old_int1Ch; DS:DX — адрес обработчика
old_int1Ch dd?; здесь хранится адрес предыдущего обработчика
start_position dw 0; позиция на экране, в которую выводится текущее время
; обработчик для прерывания 1Ch
; выводит текущее время в позицию start_position на экране
; (только в текстовом режиме)
int1Ch_handler proc far
pusha; обработчик аппаратного прерывания
push es; должен сохранять ВСЕ регистры
push cs; на входе в обработчик известно только
pop ds; значение регистра CS
mov ah, 02h; Функция 02h прерывания 1Ah:
int 1Ah; чтение времени из RTC,
jc exit_handler; если часы заняты — в другой раз
; AL = час в BCD-формате
call bcd2asc; преобразовать в ASCII,
mov byte ptr output_line[2], ah; поместить их в
mov byte ptr output_line[4], al; строку output_line
mov al, cl; CL = минута в BCD-формате
mov byte ptr output_line[10], ah
mov byte ptr output_line[12], al
mov al, dh; DH = секунда в BCD-формате
mov byte ptr output_line[16], ah
mov byte ptr output_line[18], al
mov cx, output_line_l; число байт в строке — в СХ
pop es; адрес в видеопамяти
mov di, word ptr start_position; в ES:DI
mov si, offset output_line; адрес строки в DS:SI
rep movsb; скопировать строку
pop ds; восстановить все регистры
jmp cs:old_int1Ch; передать управление предыдущему обработчику
; преобразует старшую цифру упакованного BCD-числа из AL в ASCII-символ,
; который будет помещен в АН, а младшую цифру — в ASCII-символ в AL
bcd2asc proc near
and al, 0Fh; оставить младшие 4 бита в AL
shr ah, 4; сдвинуть старшие 4 бита в АН
or ах, 3030h; преобразовать в ASCII-символы
; строка «00h 00:00» с атрибутом 1Fh (белый на синем) после каждого символа
output_line db ‘ ‘, 1Fh, ‘0’, 1Fh, ‘0’, 1Fh, ‘h’, 1Fh
db ‘ ‘, 1Fh, ‘0’, 1Fh, ‘0’, 1Fh, ‘:’, 1Fh
db ‘0’, 1Fh, ‘0’, 1Fh, ‘ ‘, 1Fh
output_line_l equ $ — output_line
Если в этом примере вместо ожидания нажатия на клавишу поместить какую-нибудь программу, работающую в текстовом режиме, например tinyshell из главы 1.3, она выполнится как обычно, но в правом верхнем углу будет постоянно показываться текущее время, то есть такая программа будет осуществлять два действия одновременно. Именно для этого и применяется механизм аппаратных прерываний — они позволяют процессору выполнять одну программу, в то время как отдельные программы следят за временем, считывают символы из клавиатуры и помещают их в буфер, получают и передают данные через последовательные и параллельные порты и даже обеспечивают многозадачность, переключая процессор между разными задачами по прерыванию системного таймера.
Разумеется, обработка прерываний не должна занимать много времени: если прерывание происходит достаточно часто (например, прерывание последовательного порта может происходить 28 800 раз в секунду), его обработчик обязательно должен выполняться за более короткое время. Если, например, обработчик прерывания таймера будет выполняться 1/32,4 секунды, то есть половину времени между прерываниями, вся система будет работать в два раза медленнее. А если еще одна программа с таким же долгим обработчиком перехватит это прерывание, система остановится совсем. Именно поэтому обработчики прерываний принято писать исключительно на ассемблере.
Пусть у нас есть собственный обработчик программного прерывания, который вызывают обработчики двух аппаратных прерываний, и пусть эти аппаратные прерывания произошли сразу одно за другим. В этом случае может получиться так, что второе аппаратное прерывание осуществится в тот момент, когда еще не закончится выполнение нашего программного обработчика. В большинстве случаев это не приведет ни к каким проблемам, но, если обработчик обращается к каким-либо переменным в памяти, могут произойти редкие, невоспроизводимые сбои в его работе. Например, пусть в обработчике есть некоторая переменная counter, используемая как счетчик, считающий от 0 до 99:
mov al, byte ptr counter; считать счетчик в AL,
cmp al, 100; проверить его на переполнение,
Учим систему страничной адресации и обработке прерываний
Приветствую. Сегодня поговорим обо всём понемногу. Введём в нашу наработку paging, разберёмся с прерываниями и их видами. Напишем несколько функций, добавим сие в код из предыдущего поста.
1) Paging.
Все, наверно, уже покопались в манах и знают, что за лепота этот paging. Вырываемся за пределы 1 Мб и можем адресовать все 4 гигабайта оперативки. Но зачем нам в нашей скромной поделке 4 Гб (тем более что не у всех есть столько RAM’а). Нам вполне будет достаточно 32Мб (ну, это тоже очень много, но давайте помечтаем). Итак, будем использовать 4Кб’айтные страницы. Так, теперь считаем – 1024 страницы описывают 4Мб. Нам, значит, нужно всего-то 1024*8 страниц. Иными словами нужно завести 8 таблиц и 1 каталог. Ещё, для удобства, страницы будут тождественными т.е логический и физический адреса совпадают. Теперь, давайте напишем функцию, которая сформирует всё необходимое.
.set_cat:
mov edi,100000h;базовый адрес директории
mov eax,101007h;базовый адрес таблицы страниц и флаги
mov cx,8 ;8*4Мб=32Мб
.fill_cat_usef: ;опишем таблицы страниц
stosd
add eax,1000h
loop .fill_cat_usef
mov cx,1016 ;а остальное забьём нулями
xor eax,eax
mov eax,00000007h
mov ecx,1024*8;32Mb
.fill_page_table: ;теперь опишем страницы
stosd
add eax,1000h
loop .fill_page_table
;End;
mov eax,00100000h;1 Mb;и установим базовый адрес первого каталога в cr3
mov cr3,eax
mov eax,cr0
or eax,80000000h
Возможно у вас возникнет вопрос, зачем же забивать нулями другие каталоги? А не забываем, что располагать таблицы, как и каталоги, можно по адресам, кратным 1000h.
2) Interrupts.
И с прерываниями PM преподносит сюрпризы: нет больше 4-ых байтных векторов прерываний, как в RM. На их место пришли Interrupt Gates (для нас сейчас важны именно они), Trap Gates, Task Gates – 64-битные структуры, находящиеся в IDT. И структура такова:
Здесь назначение полей ясно (тем более, что некоторые из них встречались нам ещё в segment descriptor’ах). Мы пока рассмотрим только Interrupt Gates. IDT нужно составить до перехода в PM, загрузить размер и смещение в регистр IDTR (его структура аналогична структуре GDTR) командой lidt. Кстати, IDT – сегмент (скажем так, антипод GDT, которая просто в линейном адресном пространстве находится) т.е она располагается в определённом сегменте. Например, если вы до перехода в PM описали, к примеру, 4Кб памяти, а располагаете IDT за этими пределами, и разрешите прерывания после перехода в PM, то всё рухнет.
Еще пара замечаний: Intel зарезервировала прерывания 0-31. Туда нужно класть обработчики для этих exception’ов. Среди них ошибка деления на 0 (#DE), исключение общей защиты #GP и другие не менее весёлые вещи. Поэтому нужно базовый адрес прерываний сместить т.е сделать так, чтобы IRQ0 занимало 32 позицию и.т.д. Сразу оговорюсь, что использовать будем старую-добрую микросхему i8259a. Конечно, можно использовать и APIC – Advanced Programmable Interrupt Controller (кстати, его нужно ручками включить), но нам просто не придётся пользоваться преимуществами такового (расширение кол-ва аппаратных прерываний, приспособленность под многопроцессорность и.т.д). Надеюсь, все помнят, что такое ICW (Initialization Control Word) и OCW (Operation Control Word)? Давайте повторим, что для чего используется (структуру приводить не буду):
ICW:
1) Определение особенности последовательности приказов.
2) Определение базового адреса (вот о чём я говорил).
3) Связь контроллеров.
4) Дополнительные особенности обработки прерываний.
OCW:
1) Управление регистром масок IMR.
2) Управление приоритетом.
3) Общее управление контроллером.
Процедура инициализации выглядит следующим образом:
Init_PIC:
mov al,11h;ICW1 – прерывание по перепаду сигнала, схемы подключены каскадно
out 20h,al;Мастеру
out 0A0h,al;Слэйву
mov al,20h;ICW2;базовый адрес (он абстрактный! Не путайте с адресом в RAM)
out 21h,al;Мастеру одно значение
mov al,28h;Слэйву другое
out 0A1h,al
mov al,04h;ICW3 – слэйв к входу IRQ2 подключён
out 21h,al
mov al,02h
out 0A1h,al
mov al,11h;ICW4 – сбрасываем флаг наличия прерывания вручную и используем камень Pentium
out 21h,al
mov al,01h
out 0A1h,al
ret
Теперь построим IDT:
IDT:
dd 0,0; 0
dd 0,0 ; 1
dd 0,0
dd 0,0 ; 3
;…. Убрал для краткости!
dd 0,0 ; 12
dw GP_handler and 0FFFFh,08h, 1000111000000000b, GP_handler shr 16 ; 13 #GP
dd 0,0 ; 14
;…. Я просто для краткости так написал. Забейте хоть тем же dup’ом это свободное место,
;чтоб не писать наскучившее dd 0,0
dd 0,0 ; 30
dd 0,0 ; 31;Здесь заканчиваются зарезервированные Intel номера ;gate’ов
dw timer and 0FFFFh, 08h, 1000111000000000b, timer shr 16 ; IRQ 0 — системный таймер
dw keyboard and 0FFFFh , 08h, 1000111000000000b, keyboard shr 16 ; IRQ 1 — клавиатура — ;предлагаю вам самим написать обработчик
dd 0,0;IRQ2
dd 0,0;IRQ3
dd 0,0;IRQ4
dd 0,0;IRQ5
dd 0,0;IRQ6
label IDT_size at $-IDT
IDTR dw IDT_size-1
dd IDT+10000h
Так, а зачем здесь пишем and’ы и shr’ы? – спросите вы. Вот тут трюк: мы же расположим весь код для PM за org’ом (смотрим код в предыдущей статье), в том числе и interrupt handlers… вот здесь и есть уловка: мы составляем 32-битный адрес из 2-х половинок, имея на руках лишь адрес обработчика. Вообще в этом нет ничего магического, просто нужно понимать, что за значение будет в этом двойном слове.
Теперь возникает вопрос, а почему же на местах многих gate’ов нули? А вот почему – совершенно не нужно писать все обработчики, ведь это, мягко говоря тяжело. Проще написать обработчик #GP, ведь, не найдя gate для прерывания, процессор генерирует пресловутое General Protection Fault.
Обработчики аппаратных прерываний вольны делать всё, что им вздумается, но должны сбрасывать флаг наличия прерывания – слать сигнал EOI – End Of Interrupt (мы же так сконфигурировали PIC, верно?).
К примеру вот так:
mov al, 20h
out 20h, al
Давайте напишем простой обработчик для таймера:
timer:
push ax
mov al, 20h
out 20h, al
pop ax
jmp int_EOI
int_EOI: ;вот здесь посылаем и Master’у и Slave’у EOI
push ax
mov al,20h
out 20h,al
out 0a0h,al
pop ax
iretd ;возврат из прерывания
int_EOI удобно использовать для всех обработчиков прерываний.
Ладно, теперь немного про исключения. Когда они происходят стек выглядит так:
Здесь error_code выталкиваем из стека и работаем с ним. Остальным займётся инструкция iretd.
Замечу, что тут содержимое ещё зависит от того, переключали ли мы ring’ или нет.
Структура error_code:
Где:
1) EXT — показывает, что сбой произошёл в обработчике прерывания или исключения.
2) IDT — когда установлен, показывает, что поля индекса относится к IDT.
3) TI — когда бит IDT не установлен, показывает, что икать нужно в GDT или LDT.
4) Segment Selector Index — показывает номер дескриптора (в GDT и LDT) или gate в IDT.
Вообще индекс обработчика исключения полезная штука. Он нам пригодится, когда наша поделка станет побольше.
Теперь давайте скомпануем код. Я схитрю: предоставлю вам возможность потренироваться. Давайте вы попробуете самостоятельно написать рабочий код. Функции мы уже написали.
Дам несколько ‘подсказок’:
1) IDT расположите до org’a (короче вместе с GDT).
2) IDTR загружаем до перехода в PM.
3) Обработчики прерываний распологаем за org’ом.
4) К текстовой видеопамяти можно обращаться как раньше.
5) Не забываем разрешить ВСЕ прерывания после того, как в PM проинициализируем PIC!
6) Paging инициализируем уже в PM.
И ещё, напишите простой обработчик для клавы.
Если будут проблемы — обращайтесь. До следующего поста.
****Исправление****
Функция set_pages сперва была написана неправильно т.к там описывалась всего 1 таблица, вместо 8. Код исправлен.
Перхватываем прерывание 08h
Аппаратные интерфейсы, описанные в книге, в IBM — PC-совместимом компьютере «живут» в специфическом архитектурном окружении. Эту специфику приходится учитывать при проектировании аппаратной части устройств, чтобы обеспечить с ними эффективное программное взаимодействие. В этой главе вкратце рассматриваются особенности процессоров х86 и связанные с этими особенностями распределение памяти, организация ввода-вывода и прерываний. Здесь же рассматривается традиционный контроллер DMA, системные средства измерения времени, а также способы внедрения собственных расширений BIOS и нетрадиционной (бездисковой) загрузки ПО в специализированные компьютеры на базе IBM PC.
вперед к началу Главы вернуться назад
Основную часть физического адресного пространства PC занимает оперативная память (ОЗУ), начинающаяся с нулевого адреса. В нее вклинивается область адресов A0000h-FFFFFh — Upper Miory Area (UMA), 384 Кбайт — верхняя память, зарезервированная со времен IBM PC для системных нужд. В UMA размещаются области буферной памяти адаптеров шины (E)ISA (например, видеопамять) и постоянная память (BIOS с расширениями). ОЗУ продолжается и за областью UMA. Под самой верхней границей физического адресного пространства имеется образ памяти системной ROM BIOS.
Для доступности сервисов BIOS в реальном режиме все ПК имеют образ ROM BIOS в адресах E0000h-FFFFFh или F0000h-0FFFFFh. Кроме того, образ BIOS должен находиться и под самой верхней границей адресного пространства, поскольку все процессоры х86 по аппаратному сбросу стартуют с адреса начала последнего параграфа памяти (FFFF0h — 8086/88, FFFFF0h — 80286 и 386SX, FFFFFFF0h — 386DX и выше с 32-разрядной шиной адреса, FFFFFFFF0h — Р6 и выше с 36-разрядной шиной адреса).
nbsp; Для компьютеров класса АТ-286 и 386SX с 24-битной шиной адреса верхняя граница оперативной памяти — FDFFFFh (максимальный размер 15,9 Мбайт). Область FE0000h-FFFFFFh содержит образ ROM BIOS, обращение к этой области эквивалентно обращению к ROM BIOS по адресам 0E0000h-0FFFFFh.
Для ПК на процессорах 386DX и выше с 32-битной шиной адреса теоретический предел объема ОЗУ — почти 4 Гбайт, верхний образ BIOS находится в адреса: FFFE0000h-FFFFFFFFh. Для ПК на процессорах Р6+ с 36-битной шиной адрес; предел объема ОЗУ — почти 64 Гбайт и верхний образ BIOS находится в адресам FFFFE0000h-FFFFFFFFFh.
Области физических адресов, не занятые ОЗУ и ROM BIOS, могут быть использованы устройствами шин (E)ISA и PCI (AGP).
Для памяти адаптеров, устанавливаемых в шину ISA, безусловно, доступна часть области адресов UMA A0000h-EFFFFh или A0000h-DFFFFh (до начала ROM BIOS). В этой области располагаются и модули расширений BIOS (см. п. 12.7.1) Карты ISA могут иметь память и в области FE0000h-FFFFFFh, но она программно доступна лишь в защищенном (и большом реальном) режимах процессора. Для отображения этой области памяти на шину ISA (а не ОЗУ) в CMOS Setup пpeдусмотрен параметр Miory Hole At 15-16М, но его включение не позволит использовать оперативную память свыше 15 Мбайт.
Поскольку шина ISA имеет 24-разрядную шину адреса, ведущие устройства этих шин (ISA Bus Master) способны обращаться к памяти (ОЗУ и память адаптеров в пределах первых 16 Мбайт (000000-FFFFFFh). To же ограничение касается стандартного контроллера DMA, которым могут пользоваться устройства шины ISA (и иные устройства системной платы).
Шина PCI имеет 32-разрядную шину адреса, так что ее ведущим устройствам доступна вся физическая память1. Для устройств PCI могут выделяться любые области адресов, свободные от ОЗУ, ROM BIOS и устройств ISA. Области адресов памяти, используемые каждым устройством PCI, описаны в заголовках их конфигурационных пространств. Эти данные требуются при распределении ресурсов и настройке мостов PCI в процессе инициализации шины.
12.2. Пространство ввода-вывода
вперед к началу Главы вернуться назад
AT и PS/2 | PC/XT | Назначение |
---|---|---|
000-00F | 000-00F | Контроллер DMA#1 8237 |
010-01F | нет | PS/2 — расширение DMA#1 |
020-021 | 020-021 | Контроллер прерываний #1 — 8259А |
040-05F | 040-043 | Таймер (PC/XT:8253, AT:8254) |
060 | 060 | Диагностический регистр POST (только запись) |
нет | 060-063 | Системный интерфейс 8255 |
060, 064 | нет | Контроллер клавиатуры AT 8042 |
061 | Нет | Источники NMI и управление звуком |
070-07F | Нет | Память CMOS и маска NMI |
080 | Нет | Диагностический регистр |
080-08F | 080-083 | Регистры страниц DMA |
090-097 | Нет | PS/2 микроканал, арбитр |
Нет | 0А0 | Маска NMI |
0A0-0BF | нет | Контроллер прерываний #2 — 8259А |
0C0-0DF | нет | Контроллер DMA #2 8237А-5 |
0F0-0FF | нет | Сопроцессор 80287 |
100-1EF | нет; | PS/2 управление микроканалом |
170-177 | нет | Контроллер НЖМД #2 (IDЕ#2) |
1F0-1F7 | нет | Контроллер НЖМД#1 (IDЕ#1) |
200-207 | 200-20F | Игровой адаптер |
нет | 210-217 | Блок расширений |
238-23F | нет | COM4 |
278-27F | 278-27F | Параллельный порт LPT2 (LPT3 при наличии MDA) |
нет | 2А2-2АЗ | часы МSМ48321RS |
2C0-2DF | 2C0-2DF | EGA#2 |
2Е0-2Е7 | нет | COM4 |
2E8-2EF | нет | COM4 |
2F8-2FF | 2F8-2FF | COM2 |
300-31F | нет | Плата прототипа |
нет | 320-32F | Жесткий диск XT |
338-33F | нет | COM3 |
370-377 | нет | Контроллер НГМД#2 |
376-377 | нет | Порты команд IDЕ#2 |
378-37F | 378-37F | Параллельный порт LPT1 (LPT2 при наличии MDA) |
380-38F | 380-38F | Синхронный адаптер SDLC/BSC #2 |
3A0-3AF | ЗА0-ЗА9 | Синхронный адаптер BSC#1 |
ЗВ0-ЗВВ | ЗВ0-ЗВВ | Монохромный адаптер (MDA) |
ЗВ4-ЗС9 | нет | PS/2 видеосистема |
3BC-3BF | 3BC-3BF | Параллельный порт LPT1 платы MDA |
3C0-3CF | 3C0-3CF | EGA#1 |
3C0-3DF | 3C0-3DF | VGA |
3D0-3DF | 3D0-3DF | CGA/EGA |
ЗЕ0-ЗЕ7 | нет | COM3 |
3E8-3EF | нет | COM3 |
3F0-3F7 | 3F0-3F7 | Контроллер НГМД#1 |
3F6-3F7 | нет | Порты команд IDE#1 |
3F8-3FF | 3F8-3FF | СОМ1 |
Каждой шине назначается своя область адресов ввода, поэтому дешифратор адресов, расположенный на системной плате, при чтении открывает соответствующие буферы данных, так что реально считываться будут данные только с одной шины. При записи в порты данные (и сигнал записи) могут распространяться по всем шинам компьютера. В стандартном распределении адреса 0h-0FFh отведены для устройств системной платы. При наличии (и разрешении работы) периферийных устройств на системной плате чтение по этим адресам не распространяется на шины расширения. Для современных плат со встроенной периферией и несколькими шинами (ISA, PCI) распределением адресов управляет BIOS через регистры конфигурирования чипсета. |
12.3. Аппаратные прерывания вперед к началу Главы вернуться назад |
Имя (номер 1 ) | Вектор | Контроллер/маска | Описание |
---|---|---|---|
NMI | 02h | Контроль канала, паритет (в XT — сопроцессор) | |
IRQ0 | 08h | #1/1h | Таймер (канал 0 8253/8254) |
IRQ1 | 09h | #1/2h | Клавиатура |
IRQ2 | 0Ah | #1/4h | XT — резерв, AT — недоступно (подключается каскад IRQ8-IRQ15) |
IRQ8 | 70h | #2/1h | CMOS RTC — часы реального времени |
IRQ9 | 71h | #2/2h | Резерв |
IRQ10 | 72h | #2/4h | Резерв |
IRQ11 | 73h | #2/8h | Резерв |
IRQ12 | 74h | #2/10h | PS/2-Mouse (резерв) |
IRQ13 | 75h | #2/20h | Математический сопроцессор |
IRQ14 | 76h | #2/40h | HDC — контроллер НЖМД |
IRQ15 | 77h | #2/80h | Резерв |
IRQ3 | 0Bh | #1/4h | COM2, COM4 |
IRQ4 | 0Ch | #1/10h | COM1, COM3 |
IRQ5 | 0Dh | #1/20h | XT — HDC, AT — LPT2, Sound (резерв) |
IRQ6 | 0Eh | #1/40h | FDC — контроллер НГМД |
IRQ7 | 0Fh | #1/80h | LPT1 — принтер |
1 Запросы прерываний 0, 1, 8 и 13 на шины расширения не выводятся. |
Каждому устройству, для поддержки работы которого требуются прерывания, должен быть назначен свой номер прерывания. Назначения номеров прерываний выполняются с двух сторон: во-первых, адаптер, нуждающийся в прерываниях, должен быть сконфигурирован на использование конкретной линии шины (джамперами или программно). Во-вторых, программное обеспечение, поддерживающее данный адаптер, должно быть проинформировано о номере используемого вектора. В процессе назначения прерываний может участвовать система РnР для шин ISA и PCI, для распределения линий запросов между шинами служат специальные параметры CMOS Setup. 12.3.1. Совместное использование прерываний 12.4. Прямой доступ к памяти — DMA |
Номер канала | DMA# | 0 1 | 1 | 2 | 3 | 4 2 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|---|
Стандартное назначение | XT | MRFR | — | FDD | HDD | Отсутствуют | |||
АТ | — | — | FDD | — | Каскад | — | — | — | |
Разрядность, байт | 1 | 2 с четного адреса | |||||||
Макс. размер блока | 64 Кбайта | 128 Кбайт, четный | |||||||
Граница блока | Кратна 1000h | Кратна 2000h | |||||||
Регистр страниц | 8 бит А16-А23 | 7 бит А17-А23 | |||||||
Адреса регистров: | |||||||||
— страниц | 087 | 083 | 081 | 082 | 08F | 08В | 089 | 087 | |
— адреса | 000 | 002 | 004 | 006 | ОСО | ОС4 | ОС8 | ОСС | |
— счетчика | 001 | 003 | 005 | 007 | ОС2 | ОС6 | ОCA | ОСЕ |
1 Канал 0 в XT использовался для регенерации памяти (MRFR). 2 Канал 4 доступен только в PS/2 МСА. |
16-битные каналы DMA 5-7 могут быть использованы интеллектуальными устройствами для прямого управления шиной ISA (bus mastering), при этом контроллер DMA фактически лишь играет роль арбитра шины. 12.4.1. Контроллер прямого доступа 8237А |
8237#1 | 8237#2 | R/W, Назначение регистров |
000, 002 004, 006 | 0С0, 0С4 0С8, 0СС | W — регистры начального адреса для каналов 0-3 (8237#1 ) и 4-7 (8237#2) R — регистры текущего адреса тех же каналов |
001, 003 005, 007 | 0С2, 0С6 0CA, 0CE | W — начальное значение счетчика передач для каналов 0-3 (8237#1) и 4-7 (8237#2) R — текущее значение счетчика передач тех же каналов |
005, 007 | 0СА, 0СЕ | W, Command Register — конфигуратор контроллера. Бит 7: 1 —активный уровень (DACK# — высокий, 0 — низкий); бит 6: 1 — активный уровень (DRQ — низкий, 0 — высокий); бит 5: 1 — режим расширенной записи (должен быть 0); бит 4: 0 — фиксированный приоритет, 1 — циклический; бит 3: 1 — укороченный цикл обмена (должен быть 0); бит 2: 1 — запрет работы контроллера; бит 1: 1 — фиксация адреса 0 канала (должен быть 0); бит 0: 1 — передача память-память (в PC не используется, должен быть 0) |
008 | 0D0 | R, Status Register — состояние каналов. Биты 4-7: запросы каналов 0-3; биты 0-3: завершение цикла каналов 0-3 |
009 | 0D2 | W, Request Register — регистр программных запросов. Биты 7-3 не используются; бит 2: 1 — установка, 0 — сброс бита запроса; биты 1-0: выбор канала (00 — 0; 01 — 1; 10 — 2; 11 — 3> |
00A | 0D4 | W, Single Mask Bit Register — управление масками. Биты 7-3 не используются; бит 2: 1 — установка, 0 — сброс бита маски; биты 1-0: выбор канала (00 — 0; 01 — 1; 10 — 2; 11 — 3) |
00В | 0D6 | W, Mode Register — режимы работы каналов. Биты 7-6: режим передачи (00 — по запросу, 01 —одиночный, 10 — блочный, 11 — каскадирование); бит 5: 0 — инкремент, 1 — декремент адреса; бит 4: 1 — разрешение автоматической реинициализации биты 3-2: тип передачи (00 — холостой, проверка канала, 01 — запись в память, 10 — чтение памяти, 11 — недопустимо); биты 1-0: выбор канала (00 — 0; 01 — 1; 10 — 2; 11 — 3) |
00C | 0D8 | W, Clear Byte Pointer Flip/Flop — сброс триггера младшего/старшего байта |
00D | 0DA | W, Master Clear — общий сброс 8237 (вывод любого байта в регистр вызывает сброс) |
00E | 0DC | W, Clear Mask Register — общий сброс масок всех каналов (вывод любого байта в регистр вызывает сброс) |
00F | 0DE | W, All Mask Register Bits — регистр масок всех каналов. Биты 0-3: маски каналов 0-3 (0 — канал разрешен, 1 — замаскирован); биты 4-7 не используются |
Стандартный контроллер DMA на шине ISA с частотой 8 МГц работает на половинной частоте и требует для одиночной передачи не менее пяти своих тактов. Длительность одиночного цикла составляет 1,125 мкс. В блочных передачах пропускная способность DMA достигает 1 Мбайт/с для 8-битных каналов и 2 Мбайт/с для 16-битных (время цикла составляет 1 мкс). На современных компьютерах контроллер DMA реализуется чипсетом системной платы; при сохранении программной совместимости с 8237А он может работать на шине гораздо быстрее. Количество тактов шины на один цикл может программироваться опциями BIOS Setup. |
12.5. Процессоры х86 вперед к началу Главы вернуться назад |
Все программы в IBM PC-совместимом компьютере исполняются центральным процессором, принадлежащим к семейству х8б. Любое устройство для процессора представляет собой лишь набор регистров (ячеек), отображенных в пространство памяти и (или) ввода-вывода, и необязательно источник аппаратных прерываний. Современные процессоры х86, работающие в защищенном режиме, имеют довольно сложные механизмы виртуализации памяти, ввода-вывода и прерываний, из-за которых приходится различать физические и логические пространства (адреса памяти и ввода-вывода) и события (операции ввода- вывода, прерывания). Физический адрес ячейки памяти или порта ввода-вывода — это адрес, формируемый для обращения к данной ячейке на физических шинах компьютера (системной шине процессора, шине PCI, ISA). Логический адрес — это тот адрес, который формируется исполняемой программой (по замыслу программиста) для доступа к требуемой ячейке. Физическая операция ввода-вывода или обращения к памяти — это процесс (шинный цикл), во время которого генерируются электрические сигналы, обеспечивающие доступ к данной ячейке (порту). Логическая операция — это исполнение программной инструкции (команды) обращения к интересующей ячейке. огическая операция не всегда порождает ожидаемую физическую операцию: при определенных условиях она может блокироваться средствами защиты процессора, вызывая даже принудительное завершение программы, или же эмулироваться, создавая иллюзию физического исполнения. 12.5.1. Возможности адресации памяти процессорами различных поколений 12.5.2. Проблемы страничной переадресации Для обращения программы к пространству ввода-вывода предназначены всего четыре инструкции процессора: IN (ввод из порта в регистр процессора), OUT (вывод в порт из регистра процессора), INS (ввод из порта в элемент строки памяти) и OUTS (вывод элемента из строки памяти в порт). Последние две инструкции, появившиеся с процессором 80286, могут использоваться с префиксом повтора REP , что обеспечивает быструю пересылку блоков данных между портом и памятью. Обмен данными с портами, при котором применяют строковые инструкции ввода-вывода, получил название РIO (Programmed Input/Output — программированный ввод-вывод). Скорость такого обмена превышает скорость стандартного канала прямого доступа (DMA), правда, DMA в отличие от РIO почти не занимает процессорного времени. 12.5.4. Прерывания 12.6. Аппаратные средства измерения времени |
Порт, R/W | Назначение регистров | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
040 RW | Счетчик 0 — системные часы. Режим 011, LSB/MSB, Binary, константа счетчика равна 0 (соответствует коэффициенту деления 65 536) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
041 RW | Счетчик 1 —регенерация памяти. Режим 010, LSB, Binary, константа счетчика — равна 12h(18) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
042 RW | Счетчик 2 — генератор звука, измерение времени. Вход GATE от бита 0 порта В 8255 (061). Режим 011, LSB/MSB, Binary, значение счетчика определяет высоту тона | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
043 W | Управляющий регистр. Биты 7, 6 — выбор счетчика 0, 1, 2. Биты 5, 4 — режим обращения: 00 —защелка текущего значения; 01 — LSB —только младший байт; 10 — MSB — только старший байт; 11 — LSB/MSB — сначала младший, затем старший байты. Биты 3-1 — режим счетчика: 000 — прерывание по счетчику; 001 — ждущий мультивибратор (одновибратор, у 8254 несколько отличается от 8253); х10 — генератор коротких импульсов заданной частоты; х11 — генератор меандра; 100 — счетчик событий с разрешением; 101 — счетчик событий с перезапуском. Бит 0 — 0=Bin (двоичный счет), 1 =BCD — (двоично-десятичный счет) |
Индекс | Назначение |
---|---|
00h-09h, 32h (37 в PS/2) | Ячейки RTC в BCD-формате: 00 — секунды; 01 — секунды будильника; 02 — минуты; 03 — минуты будильника; 04 — часы; 05 — часы будильника; 06 — день недели; 07— день месяца; 08 — месяц; 09 — год (2 младшие цифры); 32h — век-1 (2 старшие цифры года); 37h — век-1 (2 старшие цифры года) в PS/2 |
0Ah | RTC Status Register А (регистр статуса А): бит 7 — обновление времени (0 — готов к чтению); биты [6:4] — делитель частоты (для кварца на 32,768 кГц — 010); биты [3:0] — 0110 — выходная частота меандра 1 024 Гц |
0Bh | RTC Status Register В (регистр статуса В): бит 7 — остановка часов (0 — нормальный ход); бит 6 — разрешение периодических прерываний (0 — запрещено); бит 5 — разрешение прерывания от будильника (0 — запрещено); бит 4 — разрешение прерывания по окончании смены времени (0 — запрещено); бит 3 (см. также регистр OAh) — разрешение выходного меандра (0 — запрещено); бит 2 — формат BCD/BIN (0 — BCD); бит1 — 12/24-часовой режим (1 —24-часовой); бит 0 — зимнее/летнее время (0 — переключение запрещено) |
0Ch | RTC Status Register С (регистр статуса С): чтение флагов идентификаторов прерывания: бит 7 — IRQF (общий запрос прерывания); бит 6 — PF (периодические прерывания); бит 5 — AF (прерывание от будильника); бит 4 — UF (прерывание по окончании смены времени); биты [3:0] — зарезервированы |
0Dh | RTC Status Register D (регистр статуса D): бит 7 — питание ( 1 — норма, 0 — разряд батареи); биты [6:0] — зарезервированы |
Аппаратные таймеры имеют поддержку функциями BIOS (подробнее см. [1, 8, 9]). Сервисы BIOS Int 1Ah позволяют считывать и модифицировать значения системного таймера (ячейки 40: 006Eh в BIOS Data Area ), а также даты, времени и будильника CMOS RTC. 12.7. Способы запуска программ |
ПРИМЕЧАНИЕ |
Функции ROM BIOS 16-битные сервисы): Поддержка клавиатуры заключается в обработке прерываний от устройства ввода и предоставлении сервисов ввода прикладным программам. 12.8.2. Int 10h — видеосервис 12.8.3. Int 13h — поддержка дисков |
Номер функции АН | Назначение параметров | Использование регистров, указателей и таблиц | ||||
---|---|---|---|---|---|---|
DL | DH, CL, CH | AL | ES:BX | DPT/HDPT | ||
00h | Reset Disk System — сброс дисковой системы (всех контроллеров и устройств), позиционирование на нулевой цилиндр | — | — | — | — | — |
01h | Read Status of Last Operation — чтение состояния последней операции | + | — | — | — | — |
02h | Read Sectors into Memory — чтение секторов с диска в память | + | + | + | + | + |
03h | Write Sectors from Memory — запись секторов из памяти на диск | + | + | + | + | + |
04h | Verify Sectors — верификация секторов (холостое чтение без записи в память и проверка CRC/ECC) | + | + | + | — | + |
05h | Format Desired Track — форматирование трека | + | + | + | + | + |
08h | Get Drive Parameters — получение параметров диска | + 3 | + 3 | + 3 | — | + 3 |
09h 1 | Initialize Drive Parameters — инициализация таблиц параметров диска | + | — | — | — | + |
0Ah 1 | Read Long — «длинное» чтение (сектор и поле ЕСС) | + | + | + | + | + |
0Bh 1 | Write Long — «длинная» запись (сектор и поле ЕСС) | + | + | + | + | + |
0Ch 1 | Seek — поиск цилиндра | + | + | — | — | — |
0Dh 1 | Alternative Disk Reset — альтернативный сброс (не затрагивая контроллера дискет) | + | — | — | — | — |
10h 1 | Test Drive Ready — проверка готовности | + | — | — | — | — |
11h 1 | Recalibrate — рекалибровка (позиционирование на нулевой цилиндр) | + | — | — | — | — |
14h 1 | Controller Internal Diagnostics — диагностика контроллера жестких дисков | — | — | — | — | — |
15h 1 | Read DASD Туре — получение типа диска: • АН=0 — нет диска; • АН=1 —дискета, без датчика смены диска; • АН=2 —дискета, сдатчиком смены диска; • АН=3 — жесткий диск; • иные значения — код ошибки. CX: DX содержат число 512-байтных секторов на диске | + | — | — | — | — |
16h 2 | Diskette Change Line Status — проверка статуса смены дискеты: • CF=0:AH=0 — смены носителя не было; • CF=1:АН=1 —недопустимый номер диска; • АН=6 — была смена диска или определение смены не поддерживается; • АН=80h— дисковод не готов или не установлен; • иные значения — код ошибки | + | — | — | — | — |
17h 2 | Set Diskette Type for Format — установка типа дискеты для форматирования (перед форматированием) | + | + 3 | |||
18h 2 | Set Media Type for Format — установка типа носителя (для форматирования) | + | + 3 | — | — | — |
20h 2 | Get Media Type — получение типа установленного носителя | + | — | — | — | — |
24h 1 | Set Multiple Mode — установка параметров режима многосекторного обращения (в AL — число секторов за операцию) | + | — | + | — | — |
25h 1 | Identify Drive ATA — идентификация накопителя (только для ATA—дисков) | + | — | — | — | — |
Формально традиционный сервис позволяет работать с дисками, имеющими до 1024*256*63=16 515 072 секторов (около 8,4 Гбайт). Ряд операционных систем имеет ошибку, не позволяющую использовать полный объем, допустимый данным сервисом. Для дисков объемом более 15 481 935 секторов следует пользоваться только функциями расширенного сервиса (см. ниже). Однако при работе с устройствами АТА имеется еще и барьер в 528 Мбайт. Дело в том, что контроллер жесткого диска АТА, на который ориентированы драйверы Int 13h , имеет только 4-битный регистр номера головки (а в BIOS — 6 бит). Правда, этот же контроллер способен принимать 16-битный номер цилиндра (в BIOS — 10 бит). Понятно, что непосредственно без искажений через эти два фильтра (формат вызова и формат регистров контроллера) может пройти только вызов с самыми жесткими ограничениями по каждой координате. Тогда ограничение, полученное тем же перемножением диапазонов координат, получается около 528 миллионов байт: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Расширенный сервис BIOS вперед к началу Главы вернуться назад | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Чтобы получить возможность работы через BIOS с дисками объема более 8,4Гбайт, потребовалось ввести новые функции дискового сервиса. 12.8.4. Int 14h — поддержка СОМ-портов 12.8.5. Int 17h — поддержка принтера 12.8.6. Int 1Ah и Int 15h—поддержка таймеров 12.8.7. PCI BIOS 12.9. Расширения ROM BIOS |
Например, для разрешения прерывания таймера нужно выполнить следующее: Как правило, операционная система защищённого режима подразумевает возврат в режим реальных адресов и выход в ту ОС, из которой её запускали (например, в MS-DOS). В таком случае необходимо предусмотреть правильное маскирование прерываний IRQ перед возвратом в такую ОС, так как обычно не все прерывания разрешены. Для корректного возврата в режим реальных адресов нужно изменить одну команду в процедуре перенаправления векторов IRQ для R-Mode: Теперь, казалось бы, наш пример должен правильно работать, но MS-DOS приготовил один неприятный «подводный камень». Дело в том, что при повторном запуске нашего примера, при условии, что в нём выполняются какие-либо процессы, длительностью более, чем примерно 2 секунды, контроллер клавиатуры генерирует символ. Если не обработать его должным образом, то клавиатура будет заблокирована, поэтому во всех наших примерах предлагается следующее:
Как видите, установка обработчика IRQ клавиатуры свелась к простой замене определяющего его макроса «IRQ_1_handler». А теперь вашему вниманию предлагается демонстрация обработки прерываний по таймеру. В приведенном ниже примере внесены следующие изменения (по сравнению с предыдущим и с учётом всего, сказанного выше):
Вот так теперь выглядит обработчик IRQ 0: Как видите, всё что он делает — это посылает контроллеру прерываний команду конца прерывания (EOI) и увеличивает значение «timer_count» на 1. И всё! Так просто! А вот так в примере разрешены прерывания и реализован алгоритм подсчёта и вывода времени: Осталось добавить, что этот пример, как и предыдущий, правильно реагирует на исключения — выводит его номер, параметры и возвращается в R-Mode, так что можете смело экспериментировать — компьютер не зависнет. Исходный текст примера вы можете скачать здесь: examp_6.asm, pmode_6.lib и examp_6.com в архиве examp_6.zip (9594 байт). Внешние прерывания и приоритеты прерыванийСистема внешних прерываний в STM8 устроена довольно хитро. Разработчики дали нам возможность ловить прерывания с любого пина, но при этом выделять по вектору на каждый пин не стали. В результате эта часть STM8L (в S- с этим как-то получше) просто утыкана разными костылями и хитростями. До кучи, кроме внешних прерываний рассмотрим настройку приоритетов прерываний. Нам обещали прерывания на всех ножках. Вот с них, то-есть с ножек, и начнем. Разрешить или запретить прерывание для конкретного пина можно через регистр GPIOx_CR2 (где x — порт, для которого настраивается прерывание). Если ножка настроена на вход, то запись 1 в соответствующий бит регистра GPIOx_CR2 разрешит прерывание для неё. Например вот-так можно настроить кнопку на STM8L-Discovery (пин C1): За исключением настройки ножек, система внешних прерываний STM8 выглядит немного по-разному в разных семействах и линейках. Сначала подробно рассмотрим, как она организована в STM8L15xx. Там хитроумные выверты разработчиков достигают предельной концентрации, а в других сериях все устроено проще. Особенно в STM8S — там система внешних прерываний очень похожа на PCINT в AVR. Первой неожиданностью (для меня, после AVR) является то, что есть две группы прерываний: прерывание порта (EXTIB, EXTID. ) без указания пина, куда прилетают прерывания с каждой ножки порта (как PCINT); и прерывание пина (EXTI0, EXTI1… EXTI7) без указания порта, куда слетаются прерывания с нескольких портов, но с одинаковых пинов. Сначала это немного выносит мозг, но потом привыкаешь. Каждую половину порта можно настроить на генерацию 1го прерывания для любого пина (EXTID, EXTIG — по названию порта) или генерацию отдельного прерывания для каждого пина (EXTI1,EXTI2. ). Настраивается это через регистры EXTI_CONF1 и 2, а точнее, через биты PxLIS и PxHIS в них. PxLIS отвечает за младшие 4 бита, а PxHIS — за старшие. Если бит установлен (1), то эта половина порта будет генерировать одно прерывание на все пины. А если бит сброшен (0) — отдельные прерывания на каждый пин: EXTI0..3 для младшей половины и EXTI4..7 для старшей. Для некоторых портов общие прерывания (которые по штуке на порт) не доступны. С ними можно работать только через прерывания пинов. К таким обделенным относятся порты А и С в STM15x. А в STM101 своими прерываниями обладают только порты B и D. Для STM8S наоборот, есть только прерывания портов, но нет никаких EXTI0, EXTI1. Это сильно снижает возможности по настройке — фронт, по которому сработает прерывание, настраивается для целого порта. Но об этом чуть ниже. Так-как в старших STM8L15x векторов на все порты трагически не хватает, было придумано переключение. Например, вектор EXTIB/G может обрабатывать прерывания с порта B или G. За переключение векторов между портами отвечают биты PFES, PHDS, PBGS в регистрах EXTI_CONFx. Если в бит PFES записана 1, то вектор EXTIE/F будет ловить прерывания с порта F, иначе — с порта E. Аналогично с битами PHDS и PBGS (Они есть только в старших моделях, на дискавери нет). Фронт или уровень, по которому сработает прерывание, настраивается через регистры EXTI_CRx. В STM8L15x таких регистров 4 штуки, в STM8L101 — 3. Биты в них зовутся PxIS и собраны парами. Например пара P0IS[1:0] отвечает за фронт, по которому сработает прерывание EXTI0. Здесь возможны четыре варианта: 00 — Падение напряжения и низкий уровень Фанаты библиотек, для настройки прерывания, могут написать что-то типа этого: Для примера, вот так можно заставить кнопку на STM8L-Discovery (пин C1) генерировать прерывание EXTI1 по падению напряжения (т.е. когда лог уровень меняется с 1 на 0): А вот так, кнопка, висящая на B3, будет настроена на генерацию прерывания EXTIB/G по любому изменению уровня: Теперь посмотрим, как правильно оформить обработчик прерывания. Interrupt_Name это название прерывания. Оно может быть любым. Например, обработчик прерывания EXTI1, в котором тупо переключается светодиод на E7: Обратите внимание, что перед выходом из прерывания надо сбросить его флаг. Иначе — день сурка — МК будет вечно крутится в этом обработчике. Флаги разбросаны в регистрах EXTI_SR1 и EXTI_SR2. Для сброса флага в него надо записать 1. Любители библиотек могут просто написать: В данном случае так и надо делать, ибо констант для сброса флагов прерываний все-равно нет, что приводит к появлению в коде волшебных чисел (1 +2 «Assembler IBM PC 8. Лабораторная работа № 3. Применение сервисных функций BIOS для работы с экраном и клавиатурой»8.1. ЦЕЛЕВЫЕ УСТАНОВКИ¨ Вывод текста на экран путём непосредственного программирования видеобуфера. ¨ Разработка прикладных программ с использованием сервисных функций BIOS для работы с экраном и клавиатурой. ¨ Введение задержки для программных операций. 8.2. МЕТОДИЧЕСКИЕ РЕКОМЕНДАЦИИ8.2.1. ВВЕДЕНИЕВ работе № 2 были рассмотрены различные системные функции DOS вывода на экран символьной информации. Однако возможности DOS весьма ограничены: она не имеет функций для изменения цвета выводимых символов и позиционирования курсора. Кроме того, в DOS отсутствуют средства формирования графических изображений. Все возможности видеосистемы компьютера можно реализовать с помощью видеофункций BIOS прерывания int 10h. Прерывание int 10h обеспечи-вает: смену видеорежима (текстовый или графический); вывод символьной и текстовой информации; смену шрифтов, настройку цветовой палитры, работу с графическим изображением. Программирование видеосистемы с помощью средств BIOS более громоздко, однако большие возможности и высокая скорость вывода обуславливают широкое использование этого метода в прикладных программах. В данной работе рассматриваются функции BIOS [10, 12] для обслуживания видеосистемы компьютера, а также функции для работы с клавиатурой. Перечислим функции, являющиеся предметом рассмотрения в лабораторной работе. Int 10h: функция 00h – установка видеорежима; функция 02h – установка позиции курсора; функция 03h – считывание позиции и размера курсора; функция 05h – установка видеостраницы; функция 06h (07h) – инициализация или прокрутка окна вверх (вниз); функция 08h – чтение символа и атрибута в позиции курсора; функция 09h – запись символа и атрибута в позицию курсора; функция 0Ah – запись символа в позицию курсора с текущим атрибутом; функция 0Eh – запись символа в режиме телетайпа с текущим атрибутом; функция 0Fh – получить режим дисплея; функция 1003h – переключение назначения старшего бита байта атрибута: мерцание/яркость, функция 13h – запись строки с заданным атрибутом в режиме телетайпа. Int 16h: функция 00h (10h) – чтение символа с клавиатуры с ожиданием; функция 01h (11h) – проверка буфера клавиатуры на наличие в нём символа; функция 02h (12h) – получение флагов (расширенной) клавиатуры. Int 15h, функция 86h – задержка. Int 1Ah, функция 00h – получение системного времени. 8.2.2. ПРЯМОЕ ПРОГРАММИРОВАНИЕ ВИДЕОБУФЕРА В ТЕКСТОВОМ РЕЖИМЕСовременные видеоконтроллеры поддерживают разнообразные текстовые и графические режимы. Текстовые режимы различаются по разрешению (число отображаемых символов по горизонтали и вертикали) и цветовой палитре (монохромный или 16-цветный режим). Для графических режимов основным признаком классификации является количество одновременно отображаемых цветов и, соответственно, количество бит видеопамяти, отводимое на каждую точку (пиксел) изображения. Различают следующие типы графических режимов: – монохромный (1-битное кодирование); – 16-цветный EGA/VGA (4-битное кодирование); – 256-цветный SVGA (8-битное кодирование); – HiColor (16-битное кодирование); – TrueColor (24-битное / 32-битное кодирование). Графические режимы VGA (SVGA) сильно устарели, а текстовые продолжают успешно применяться (см. табл. 3.2 п. 8.2.3). Всё, что изображено на мониторе – графика, текст – одновременно присутствует в памяти, встроенной в видеоадаптер. Для того чтобы изображение появилось на мониторе, оно должно быть записано в память видеоадаптера. В текстовом режиме для VGA-совместимых систем для видеопамяти отводится адресное пространство (исключая 7-й видеорежим с монохромным адаптером), начинающееся с логического адреса B800h:0000h и заканчивающееся адресом BF00h:0FFFh. Данная область разбивается на 8 секторов по числу видеостраниц (4 Кбайта на страницу). Таким образом, постраничное деление адресного пространства видеопамяти в текстовом режиме имеет следующий вид: – B800h:0000h – страница 0, смещение в диапазоне 0000h – 0FFFh – B900h:0000h – страница 1, смещение в диапазоне 0000h – 0FFFh – BF00h:0000h – страница 7, смещение в диапазоне 0000h – 0FFFh На экране отображается видеобуфер, соответствующий активной странице. В текстовых режимах для изображения каждого символа отводится 2 байта: байт с ASCII-кодом символа и байт с его атрибутом. При этом по адресу B800h:0000h находится байт с кодом символа (левый верхний угол экрана), а в B800h:0001h – атрибут этого символа; B800h:0002h – код второго символа, а в B800h:0003h – атрибут второго символа и т.д. Вообще при формировании изображения непосредственно в видеобуфере, в обход программ DOS и BIOS, все управляющие коды ASCII теряют свои управляющие функции и отображаются в виде соответствующих символов. Структура байта атрибутов приведена на рис. 3.1. Рис. 3.1. Структура байта атрибутов Из рис. 3.1 следует, что каждый символ может принимать любой из 16 возможных цветов, определяемых сочетанием младших 4-х битов. Биты 4-6 байта атрибутов задают цвет фона под данным символом. Последний бит 7, в зависимости от режима видеоадаптера, определяет либо яркость фона под данным символом (тогда фон также может принимать 16 разных цветов), либо мерцание символа (устанавливаетсяDOS по умолчанию). При загрузке машины устанавливается стандартная палитра, коды цветов которой приведены в табл. 3.1. Рассмотрим некоторые примеры. Так, в режиме мерцания значение старшего полубайта атрибута 8hобозначает не серый фон, а чёрный при мерцающем символе, цвет которого по-прежнему определяется младшим полубайтом; значение старшего полубайта 0Ch – красный фон при мерцающем символе. Переключение назначения бита 7 осуществляется подфункцией 03h функции 10h прерывания int 10h. Коды цветов стандартной палитры Двухбайтовые коды символов записываются в видеобуфер в том порядке, в каком они должны появиться на экране: первые 80*2 байт соответствуют первой строке экрана, вторые 80*2 байт – второй и т.д. При этом переход на следующую строку экрана определяется не управляющими кодами возврата каретки и перевода строки, а размещением кода в другом месте видеобуфера. Для того чтобы из программы получить доступ к видеобуферу, надо занести в один из сегментных регистров данных сегментный адрес видеобуфера. После этого, задавая те или иные смещения, можно выполнить запись в любые места (ячейки) видеобуфера. Вычислить смещение ячейки в координатах «строка-столбец» (row, clm) можно так: При большом объёме выводимых данных, информационный кадр формируется заранее в буфере пользователя, располагающегося в сегменте данных программы. Листинг 3.1. Запись строки в видеобуфер 0-страницы. ;Настроим сегментный регистр ES на страницу 0 видеобуфера, а ds на сегмент данных ;Перешлём в видеобуфер строку символов, настроив соответствующим образом ;регистры si, di и cx mov si,offset msg ;Смещение источника mov di,160*12+36*2 ;Смещение приёмника (36 столбец 13 -ой строки), mov cx,msglen ;Число пересылаемых байт cld ;Просмотр вперёд rep movsb ;)* ;Переслать строку символов с атрибутами в видеобуфер ;Остановим программу для наблюдения результата (иначе после завершения программы ;запрос DOS на ввод команды может затереть выведенную информацию) ;Поля данных в сегменте данных программы. Символы и атрибуты: 0B0h – cветло- ;бирюзовый по чёрному, 0E4h –красный по жёлтому msg db ‘*’,0B0h,’T’,0E4h,’E’,0E4,’S’,0E4,’T’,0E4,’*’,0B0h В данном фрагменте программы символьные коды выводимого сообщения перемежаются с их атрибутами. Такой способ формирования полей данных, предназначенных для прямой записи в видеопамять, становится громоздким, однако его можно существенно упростить, если выводимые символы имеют одни и те же атрибуты. Так, если мы хотим осуществить вывод символов текста из сегмента данных с единственным атрибутом 0E4h, то нам нужно просто заменить одну командную строку, отмеченную в выше приведённом фрагменте символом «*)», на три. При этом задание строки данных приобретёт привычный для нас вид. mov si,offset msg ;Смещение источника mov di,160*12+36*2 ;Смещение приёмника (36 столбец 13 -ой строки), mov cx,msglen ;Число пересылаемых байт cld ;Просмотр вперёд mov ah,0E4h ;Атрибут выводимых символов 0E4h – красный по жёлтому cycle: lodsb ;Загрузка в al очередного символа (al ← ds:si) stosw ;Выгрузка “символ + атрибут” из ах в видеобуфер (ax→es:di) loop cycle ;Повторить msglen раз ;Поля данных в сегменте данных программы. Изложенный выше способ вывода текста форматируется длиной видеостроки без учёта символов переноса или отступов от левой границы. Внесение элементарных правил текстового редактора в процедуру вывода сильно усложнит программу. В этом случае для вывода сообщений целесообразно использовать функции BIOS. Разработка структуры программ, осуществляющих просмотр произвольных видеостраниц, на которые предварительно записана информация способом прямого программирования видеобуфера, удобно производить с применением функции 05h int 10h BIOS (п. 8.2.3.2). 8.2.3. СПРАВОЧНЫЕ ДАННЫЕ ПО ФУНКЦИЯМ BIOS8.2.3.1. Прерывание int 10h. Видеофункции BIOS¨ Функция 00h. Установка видеорежима (табл. 3.2) текущей видеостраницы с очисткой экрана (быстрая очистка экрана реализуется функцией 06h и 07h). Al = видеорежим (код режима задаётся в младших 7 битах, установка в 1 старшего бита запрещает очистку экрана). Текстовые видеорежимы и страницы в стандарте VGA, поддерживаемые Режим Тип Разрешение Цвет Размер знака Адрес Страницы 3 text 80×25 16/8 9×16 B8000 0 – 7 3 (Mono) По умолчанию в DOS используется режим 3 (впрочем, корректно оформленная программа должна выполнять проверку или установку требуемого текстового режима с последующим восстановлением прежнего). ¨ Функция 02h. Установка позиции курсора. Задаёт положение курсора на экране в текстовых координатах, с которых в дальнейшем будет выводиться текст. Отсчёт номера строки и столбца ведётся от верхнего левого угла. Курсор можно установить как в текстовом, так и в графическом режиме, однако, в графическом режиме курсор не виден. BIOS поддерживает до восьми независимых курсоров – по одному на каждую страницу (см. табл. 3.2) независимо от того, какая страница является активной. Функцию 02h BIOS можно использовать в комбинации с функциями DOS для организации вывода на экран. Вызов: AH = 02h; BH = номер страницы (0,1. 7), обычно 0; ¨ Функция 03h. Считывание позиции и размера курсора. Возвращает текущие координаты состояния курсора на выбранной странице. Это даёт возможность временно перейти для работы на другое место экрана, а затем вернуться на старое место. Функцию 03hBIOS можно использовать в комбинации с функциями DOS для организации вывода на экран. Вызов: AH = 03h, BH = номер страницы (0,1. 7), обычно 0. Возврат: DH, DL = строка и столбец текущей позиции курсора, CH, CL = первая и последняя строки развёртки курсора. Вызов разрушает регистры AX, BP, SI и DI. ¨ Функция 05h. Установка видеостраницы. Устанавливает активную видеостраницу (как текстовую, так и графическую). Вызов: AH= 05h, AL= номер страницы (0. 7). Программа, устанавливающая страницу, отличную от текущей, обязана по окончании работы восстанавливать исходную. ¨ Функция 06h (07h). Инициализация или прокрутка окна вверх (вниз). Инициализирует окно с указанными координатами, пробелами ASCII с заданным атрибутом (AL = 0), или прокручивает содержимое окна вверх (вниз) на заданное число строк (AL = число строк). При прокрутке появляющиеся снизу (сверху) строки заполняются пробелами ASCII с заданным атрибутом. Функцию удобно использовать для быстрой очистки экрана или некоторого прямоугольного окна. AL = 0 – очистка, AL = N (N >0) – прокрутка на N строк; BH = атрибут символов в окне; CH, CL = координаты строки и столбца (Y,X) левого верхнего угла; DH, DL = координаты строки и столбца (Y,X) правого нижнего угла. ¨ Функция 08h. Чтение символа и атрибута в текущей позиции курсора на выбранной странице. Вызов: AH = 08h, BH = номер страницы (0. 7), обычно 0. Возврат: AH = атрибут символа, AL = ASCII-код символа. Вызов разрушает регистры BP, SI и DI. ¨ Функция 09h. Запись символа с заданным атрибутом на экран в позицию курсора. Действует как в графическом, так и в текстовом режимах. В графическом режиме символы не должны переходить на следующую строку. Все коды в AL рассматриваются как символьные и не управляют положением курсора. После вывода символа курсор смещается к следующей позиции функцией 02h. Коэффициент повторения позволяет выводить строки одинаковых символов. В текстовом режиме символ выводится с указанным в BL атрибутом. В графическом – содержимое BL влияет только на цвет символа, но не на фон под ним. Графическое изображение под знакоместом затирается. Вызов: AH =09h, AL = ASCII-код символа, BL = атрибут символа (текстовый режим) или только цвет символа (графический режим), BH = номер страницы (0,1. 7), CX = коэффициент повторения. ¨ Функция 0Ah. Запись символа с текущим атрибутом на экран в позицию курсора. Функция действует как в графическом, так и в текстовом режимах. Символ принимает атрибут, установленный ранее для этой позиции. Все ASCII-коды в AL рассматриваются как символьные и не управляют положением курсора (также как и в функции 09h). После вывода символа курсор смещается к следующей позициифункцией 02h. Вызов: AH = 0Ah, AL = ASCII-код символа, BH = номер страницы (0,1. 7), CX = коэффициент повторения. ¨ Функция 0Eh. Запись символа с текущим атрибутом в режиме телетайпа. Записывает символ ASCII в позицию курсора (предварительно установленную функцией 02h) на активной странице и смещает курсор к следующей позиции. Коды ASCII: 07h – звонок (BEL), 08h – шаг назад(BS), 0Dh – возврат каретки (CR), 0Ah – перевод строки (LF), рассматриваются как управляющие и выполняются соответствующие им действия. Остальные управляющие коды рассматриваются как символы и выводятся на экран. Действует автоматический перевод курсора на следующую строку после завершения предыдущей, а также прокрутка экрана вверх на 1 строку после заполнения самой нижней. Вызов: AH = 0Eh, AL = ASCII-код символа, BL = цвет символа (только для графического режима), BH = номер страницы (0,1. 7), по умолчанию действует активная страница. ¨ Функция 0Fh. Получить режим дисплея и номер текущей страницы. Возврат: AL = режим дисплея, AH = ширина экрана в текстовом формате BH =номер активной страницы. Вызов разрушает регистры BP, SI и DI. Пример. Процедура установки позиции курсора на текущей странице. Вход: dh = строка (0 – 25), dl = столбец (0 – 79) . ;Сохранить регистры (по необходимости) . ;Восстановить регистры ¨ Функция 10h. Подфункция 03h. Переключение бита «мерцание/яркость». Определяет назначение старшего бита 7 атрибута символа: мерцание символа или повышенная яркость фона. Вызов: AX = 1003h, BL = назначение 7-го бита атрибута: 0 – повышенная яркость, 1 – мерцание (устанавливается по умолчанию). Функция воздействует сразу на все символы экрана, у которых установлен старший бит атрибута фона. ¨ Функция 13h. Запись строки символов с заданными атрибутами. Записывает строку в текущую страницу видеобуфера, начиная с указанной позиции. Коды ASCII: 07h – звонок, 08h – шаг назад, 0Ah – перевод строки, Вызов: AH = 13h, AL = режим записи: 0 – атрибут символа в BL, строка содержит только коды символов, после записи курсор принимает исходное положение (т.е. вывод следующей строки, если не изменить позицию курсора, начинается с изначально установленной позиции); 1 – отличается от режима 0 тем, что после записи курсор остаётся в конце строки; 2 – строка содержит попеременно коды символов и атрибутов (т.е. каждый символ описывается 2 байтами – ASCII-кодом и атрибутом), после записи курсор принимает исходное положение; 3 – отличается от режима 2 тем, что по окончании вывода курсор остаётся в конце строки. BH = номер страницы (0,1. 7), BL = атрибут для режимов 0 и 1, CX = длина символьной строки (в длину входят только коды символов, но не байты атрибутов), DX = DH.DL = координаты курсора (строка, столбец) в исходной точке вывода строки на экране, ES:BP = адрес начала строки в памяти. Обратите внимание на особенность задания адреса! 8.2.3.2. Рекомендации по использованию видеосервиса BIOS1. Программы (учебный практикум), выполняемые в операционной среде DOS, используют по умолчанию текстовый режим 3, страницу 0. 2. Программы более широкого назначения должны запрашивать текущий видеорежим и страницу (функция 0Fh, int 10h) с последующим их применением в используемых функциях BIOS. Mov ah,0Fh ;Запрос текущего режима Mov v_mode, al ;Сохраним режим Mov current_page, bh ;Сохраним строку 3. Если программа выводит изображение на разные страницы, то последовательность действий с каждой страницей может быть следующей (предполагается режим по умолчанию с «0» – страницей): – установка страницы функцией 05h; – установка позиции курсора функцией 02h; – построчное форматирование текста BIOS или DOS. В дальнейшем может быть организован циклический просмотр содержания страниц путём их переключения функцией 05h, int 10h. При выходе из программы обязательно восстанавливаем искомую «0»-страницу. Сделать это, к примеру, можно так. ;Анализ буфера клавиатуры функцией DOS 06h int 21h с целью её завершения нажатием ;произвольной клавиши mov ah,06h ;Функция ввода без ожидания mov dl,0FFh ;Ввод jnz out_program ;zf=0, есть символ, на выход jmp continue ;zf=1, символа нет, продолжим работу out_program: ;Восстановим страницу функцией 05h, int 10h exit: mov ax,4C00h ;Вызов функции завершения программы Страницы видеобуфера могут быть последовательно отформатированы и способом непосредственного программирования памяти. Выбор страниц при этом осуществляется соответствующей инициализацией сегментного регистра ES (см. п. 8.2.2). Просмотр содержимого страниц также может быть выполнен путём их последовательного переключения с помощью функции 05h, int 10h. 4. Структура демонстрационной программы, исследующей функцию «мерцание – яркость фона» (функция 10h, подфункция 03h, int 10h). ;Инициализация 2-х локальных окон, каждое со своим атрибутом и текстом. При задании ;атрибутов цвета старший (7-ой по номеру) бит выбран равным «1». continue: ;Включим мерцание mov bl,1 ;Мерцание ;Введём задержку на 3 сек ;Включим повышенную яркость ;Введём задержку на 3 сек ;Анализ буфера клавиатуры функцией DOS 06h int 21h с целью её завершения нажатием jnz out_program ;zf=0, есть символ, на выход jmp continue ;zf=1, символа нет, продолжим работу out_program: ;Восстановим мерцание (по умолчанию) exit: mov ax,4C00h ;Вызов функции завершения программы 5. Если в программе организован бесконечный цикл вывода данных на экран функциями BIOS (09h, 0Ah, 0Eh, 13h), то его нельзя будет аварийно прервать с помощью нажатия клавиш Ctrl+C (т.е. выйти из программы, как это можно сделать при использовании соответствующих функций DOS). Чтобы можно было это сделать, включите в тело цикла функцию 0Bh прерывания Int 21h. 8.2.3.3. Прерывание int 16h¨ Функция 00h (10h). Чтение символа клавиатуры с ожиданием. Читает из кольцевого буфера ввода символ и скан-код. После считывания они удаляются из буфера и возвращаются в регистре AX. Если буфер пуст, ожидает ввода. Каждой клавише на клавиатуре соответствует так называемый скан-код, соответствующий только этой клавише. Этот код посылается клавиатурой при каждом нажатии и отпускании клавиши и обрабатывается в BIOS обработчиком прерывания Int 09h. Функция 00h даёт возможность получить код нажатия, не перехватывая этот обработчик. Если нажатой клавише соответствует ASCII-символ, то: AL – ASCII-код символа, AH – скан-код клавиши. Если нажатой клавише соответствует расширенный ASCII-код, то: Возврат: AL = ASCII-код символа, изображённый на клавише/00h, AH = скан-код/расширенный ASCII-код клавиши. Функция 10h (AH = 10) – усовершенствованный вариант функции 00h для расширенной клавиатуры (101/102-key). Позволяет получить расширенные ¨ Функция 01h (11h). Поверка буфера клавиатуры на наличие в нём символа. Определяет, имеются ли в кольцевом буфере ожидающие ввода символы; возвращает флаг ожидания и сам символ при его наличии. Однако символ и его скан-код не извлекаются из буфера и могут быть снова получены при повторном вызове функции 00h Int 16h. Данная функция относится к числу асинхронных: определив состояние буфера ввода, она возвращает управление про- Возврат: ZF = 1, если буфер пуст и ZF = 0, если в буфере имеется ожидающий считывания символ. В этом случае: AL = ASCII-код символа/00h, AH = скан-код клавиши/расширенный ASCII-код. Функция 11h (AH = 11h) – усовершенствованный вариант функции 01h для расширенной клавиатуры (101/102-key). Позволяет получить расширенные ASCII-коды для клавиш F11, F12, а также для ряда других комбинаций. В качестве признака управляющих клавиш или их комбинаций, помимо значения 00h, используются 0Ah, 0Dh и E0h. ¨ Функция 02h (12h). Получение флагов клавиатуры. Возвращает байт флагов клавиатуры, описывающих состояние управляющих клавиш, записанное в байте (слове) области данных BIOS по адресу 0000h:0417h. Возврат: A L=1-ый байт флагов клавиатуры. Биты байта имеют следующие значения: 0: 1 – правая Shift нажата 1: 1 – левая Shift нажата 2: 1 – Ctrl (любая) нажата 3: 1 – Alt (любая) нажата 4: 1 – режим Scroll Lock 5: 1 – режим Num Lock 6: 1 – режим Caps Lock 7: 1 – режим Insert активен Функция 12h (AH = 12h) – усовершенствованный вариант функции 02h для расширенной клавиатуры (101/102-key). Выводит такое же значение байта, как и функция 02h, по адресу 0000h:0417h, и, дополнительно, второй байт статуса клавиатуры (адрес 0000h:0418h) со следующими значениями: 0: 1 – левая Ctrl нажата 4: 1 – нажата Scroll Lock 1: 1 – левая Alt нажата 5: 1 – нажата Num Lock 2: 1 – правая Ctrl нажата 6: 1 – нажата Caps Lock 3: 1 – правая Alt нажата 7: 1 – нажата SysReg 8.2.3.4. Задержка программных операцийПрограммные задержки используются в тех случаях, когда в какой-либо точке программы надо приостановить её выполнение на некоторое время. По виду исполнения программные задержки делятся на два типа: задержки, реализуемые на основе выполнения программой «пустых» вложенных циклов, и задержки, реализуемые на основе системного таймера компьютера. В листинге 3.2 приведён пример реализации задержки первого типа. Листинг 3.2. Программная задержка на основе выполнения вложенных циклов с командой Loop. Proc delay ;Подпрограмма задержки Mov cx, N ;N – счётчик внешнего цикла Outer: push cx ;Сохраним содержание счётчик внешнего цикла Mov cx,0 ;Обеспечим максимальное число повторений (64К раз) Inner: loop Inner ;Внутренний цикл Pop cx ;Восстановим содержание счётчик внешнего цикла Loop Outer ;Повторим вешний цикл N раз В листинге 3.2 параметр N выполняет роль масштабного множителя времени задержки При этом наименьшей единицей времени (т.е. «тиком») является время выполнения внутреннего цикла, состоящего, в свою очередь, из времени исполнения 65535 раз команды Loop. Параметр Nподбирается экспериментально для получения tзад (в мсек или сек) с учётом быстродействия конкретного компьютера. Из рассмотрения данного примера очевидны недостатки данного подхода, когда требуется обеспечить выполнение временной задержки в программе, независимо от типа используемого компьютера.Поэтому разумно определять время программной задержки непосредственно по таймеру. Выходные сигналы таймера с частотой 18,2 раза в секунду не зависят от производительности компьютера и играют роль счетчика суточного времени. Реализация данного способа использует функцию 00h прерывания BIOS Int 1Ah. Int 1Аh, функция 00h. Чтение счетчика циклов таймера. Обработчик прерывания BIOS от системного таймера (Int 8) подсчитывает количество прерываний (каждые 55 мсек или 18,2 раза в секунду) в двойном слове памяти с адресом 0040h:006Сh. Данная функция возвращает накопленное значение (двоичный код) и сбрасывает его в . В регистре AL возвращается , если содержимое счетчика не превысило значения, соответствующего 24 часам (при достижении этого значения счетчик сбрасывается), иначе возвращается AL = 1. Возврат: СХ:DX – число тактов системного времени от полуночи, AL – флаг перехода через сутки. Примеры возвращаемых значений в СХ:DX: 1 сек 12h или 18, 1 минута 04 44h или 1092, 1 час 1 00 07h или 65543, 24 часа 18 00 B0h или 1 573 040. Для задержек меньших 14 секунд можно пользоваться только младшим байтом регистра DX Листинг 3.3. B данном примере установлена задержка на 5 секунд, что соответствует 91 отсчету таймера mov ah,0 ;Функция «чтения» циклов таймера int 1Ah ;Получаем значение счетчика циклов в cx:dx add dx,91 ;Добавляем 5 сек. к младшему слову в dx mov bx,dx ;Запоминаем требуемое значение в bx и выполняем ;постоянную проверку значений счетчика времени суток repeat: int 1Ah ;Вновь получаем значение счетчика cmp dx,bx ;Сравниваем с искомым jne repeat ;Если не равно, то повторяем снова, ;иначе задержка окончена Если требуется введение задержки с высокой точностью, то необходимо использовать функцию 86h прерывания BIOS Int 15h. Она позволяет определить время задержки в микросекундах. Во время выполнения задержки разрешены прерывания. Управление программе возвращается после истечения заданного времени. Int 15h, функция 86h Вызов: AH = 86h, СX:DX = время задержки в мксек. Возврат: CF = 0 – нормальное исполнение, CF = 1 – функция не поддерживается. Пример: CX:DX = 0098h:9680h = 10 000 000 мксек = 10 сек. 8.3. ВАРИАНТЫ ИНДИВИДУАЛЬНОГО ЗАДАНИЯ1. Инициализировать экран с определённым атрибутом. Наложить на него локальное окно меньшего размера с другим атрибутом цвета. В центральную часть окна вывести текст (несколько строк) из памяти с циклической реализацией скроллинга окна в несколько строк вверх и вниз. Смена типа скроллинга задаётся программной задержкой (2. 3 сек.). Предусмотреть выход из программы. 2. Инициализировать две видеостраницы, каждая со своим атрибутом и записанным текстом (некоторые символы текста обязательно должны иметь отличный от других цвет). Организовать циклическую смену видеостраниц с периодом 2. 3 сек. Предусмотреть выход из программы с восстановлением текущей страницы. 3. На экране инициализировать 2 локальных окна. Каждое окно со своим атрибутом и текстом с несколькими строками. Организовать циклическое переключение атрибутов первого окна на второе и обратно. Цикл переключения задаётся временной задержкой в 2. 3 сек. Предусмотреть выход из программы. 4. На экране инициализировать 2 локальных окна. Каждое окно со своим атрибутом и текстом в несколько строк. Организовать циклическое переключение текста из одного окна в другое с временной задержкой 2. 3 сек. Предусмотреть выход из программы. 5. На экране инициализировать окно_1 с атрибутом и текстом в несколько строк. Спустя время задержки 2. 3 сек частично наложить на него окно_2 с другим атрибутом и текстом. Процесс зациклить. Предусмотреть выход из программы. 6. На экране инициализировать локальное окно с атрибутом (и текстом), сообщить ему дрейф в горизонтальном (вертикальном) направлении. При достижении границы экрана окно изменяет дрейф в противоположную сторону. Шаг движения локального окна в пространстве экрана должен быть во много раз меньше размеров самого экрана. 7. На экране инициализировать локальное окно с атрибутом (и текстом). После нажатия командной клавиши окно начинает изменять свои размеры (пульсировать), увеличиваясь и уменьшаясь с определённым периодом. Временной шаг изменения размера окна должен быть много меньше периода 8. Инициализировать экран и локальное окно в нём со своими атрибутами. Организовать режим вывода текста в локальное окно с клавиатуры. Предусмотреть возможность редактирования текста, а также скроллинга окна при его заполнении. 9. Инициализировать экран и два небольших локальных окна в нём. С помощью клавиши организовать переключение курсора из одного окна в другое. Выбранное клавишей окно приобретает повышенную яркость. Предусмотреть выход из программы. 10. Инициализировать экран и два локальных окна в нём. В левое окно вывести первую половину таблицу ASCII, а в правое – вторую половину. Предусмотреть очистку окон и выход из программы. 11. Инициализировать экран и два локальных окна в нём (каждое со своим атрибутом и текстом). Организовать циклическое переключение бита «яркость фона/мерцание». Выход из программы должен восстанавливать значение бита по умолчанию. 12. Инициализировать экран и два локальных окна в нём. В левое окно вывести вторую половину таблицы ASCII c символами псевдографики. Используя навигацию курсора, c помощью клавиш (¬,,®,¯)организовать возможность непрерывного воспроизведения прямых линий во втором окне. 13. Разработать программу вывода текста на экран путём непосредственного программирования видеобуфера с использованием элементов форматирования (отступ от левой границы, перенос текста на следующую строку после пересечения словом правой границы). CX – число выводимых символов; Indent_L, Indent_R – поля отступа (в столбцах) слева и справа. Необходимо оптимизировать расчёт адреса видеобуфера ES:DI. Процедура должна возвращать исходное значение регистра ES. 14. Используя прямое программирование видеопамяти, заполнить несколько страниц видеобуфера с последующим просмотром их (выводом на экран) в циклическом режиме. При выходе из программы обеспечить восстановление текущей страницы. 15. Разработать программу рисования прямоугольника, используя графические символы в кодировке ASCII. Координаты верхнего левого угла (строка, столбец) и нижнего правого должны вводиться с клавиатуры после соответствующего приглашения. 8.4. КОНТРОЛЬНЫЕ ВОПРОСЫ1. Краткая характеристика возможностей, предоставляемых программисту базовой системой ввода-вывода BIOS, в сравнении с сервисными функциями DOS. 2. Назовите объём видеопамяти для изображения одного символа и, соответственно, одной видеостраницы монитора в текстовом режиме. 3. Дайте характеристику атрибута символа в видеобуфере. 4. Разработайте макросы для: ¨ очистки экрана с установкой курсора в левый верхний угол экрана; ¨ позиционирования курсора в произвольную точку экрана с запоминанием его координат в памяти с помощью переменных row и clm; ¨ вывода сообщения mes длиною leng и атрибутом цвета attrib с позиции, определяемой переменными row и clm. 5. Какая функция BIOS предоставляет пользователю исчерпывающую информацию о нажатой клавише клавиатуры. Разработка графического структурного алгоритма программыПрикладная программа, управляющая разработанным устройством, представляет собой исполняемый файл main.com. Работу устройства можно задать изначально в начале выполнения main.com, с помощью параметра D — число, заносимое в регистр RG для установки соответствующего сопротивления. 1. Запросить управляющий байт. 2. Занести в указанный порт введённый байт. 3. Ожидать сигнала с шины данных, если он получен — вывести сообщение В программе можно выделить 3 основных логических блока — занесение управляющего слова в регистр, перехват прерывания 08h, параллельное слежение за шиной данных. Блок схема работы программы (структурный графический алгоритм) приведен в Приложении данного курсового проекта. Разработка прикладной программы и описание её возможностейПо сложности программирования интерфейс ISA занимает среднее место между Centronics с одной стороны, и PCI и SCSI с другой. Особенностью программирования Centronics является абсолютная простота, т.к все сигналы доступны для программирования. ISA — доступность только шины данных и портов ввода-вывода, PCI — сложность программирования шины в целом. Данная прикладная программа управляет разработанным нестандартным периферийным устройством. Это управление довольно просто, так как изначально устройство работало абсолютно автономно, и мне пришлось вводить дополнительные управляющие регистры и делать параллельные отводы от схемы, что ввело лишь небольшие усложнения в работу устройства в целом. Программа является резидентной, т.е. находится постоянно в памяти. Загружается командой “main.com”, инициализирует устройство, и предлагает ввести байт D, где D — число, заносимое в управляющий регистр (по умолчанию оно равно 0), и выгружается “main.com u». Находясь в памяти, программа постоянно следит за шиной данных, и если пришло сообщение о включении/выключении нагревателя, выдаёт соответствующее сообщение. |