высоким разреше- нием, используя рисунок символов, состоящий из 8*14 точек, а не обычные 8*8. BIOS хранит однобайтную переменную по адресу 0040:0049, в которой содержится номер текущего режима. Байт по адресу 0040:004A дает число символов в строке в текстовом режиме. Высокий уровень. Бейсик использует операторы SCREEN и WIDTH для управления режимом экрана. PCjr использует эти операторы несколько другим способом, чем монохромный и цветной адапторы, и это будет обсуж- даться ниже. Один оператор SCREEN устанавливает режим для цветно- го адаптора. За оператором стоит номер кода, устанавливающий разрешение, где: 0 текстовый режим 1 графический режим среднего разрешения 2 графический режим высокого разрешения SCREEN 1 устанавливает графический режим среднего разрешения. Второй параметр включает и выключает цвет. Этот параметр не имеет смысла для режима высокого разрешения на цветном адапторе, пос- кольку разрешен только черно-белый режим. Для текстовых режимов 0 в качестве второго параметра выключает цвет, а 1 - включает. Оператор SCREEN 0,0 устанавливает текстовый черно-белый режим. Для графического режима ситуация обратная: 0 - включает цвет, а 1 - выключает. Поэтому оператор SCREEN 1,1 устанавливает черно-бе- лый графический режим среднего разрешения. Все режимы первоначально показываются черно-белыми. Оператор COLOR (см. [4.1.3]) должен быть использован, чтобы закрасить экран фоновым цветом. В графическом режиме одного оператора COLOR достаточно, чтобы изменить весь фон на указанный цвет. Но для текстового режима Вы должны после оператора COLOR использовать оператор CLS. В текстовых режимах в строке может быть 40 или 80 символов. Для установки требуемого числа символов в строке надо использо- вать оператор WIDTH. WIDTH 40 дает 40 символов в строке, а WIDTH 80 - 80. Другие значения недопустимы. Если оператор WIDTH исполь- зуется в графическом режиме (SCREEN 1 или SCREEN 2), то WIDTH 40 переводит экран в режим среднего разрешения, а WIDTH 80 - в режим высокого разрешения. Вот несколько примеров: 100 SCREEN 0,1: WIDTH 40 'цветной текстовый режим с 40 символами 100 SCREEN 0,1: WIDTH 40 'цветной дисплей как монохромный 100 SCREEN 0,1: WIDTH 40 'цветная графика среднего разрешения . . 500 WIDTH 80 'переводим в режим высокого разрешения Монохромный монитор может быть переведен в режим 40 символов в строке операторами SCREEN 0: WIDTH 40. Для восстановления режима с 80 символами введите WIDTH 80. В режиме с 40 символами они сохраняют свою обычную ширину, поэтому будет использоваться толь- ко левая часть экрана. Строка переносится после 40-го столбца и невозможно поместить курсор в правую половину экрана с помощью оператора LOCATE. CLS чистит только левую часть экрана. Трудно представить программу, которая использовала бы это свойство, но оно действительно позволяет программе принимать ввод (скажем, через оператор INPUT), в то время как пользователь продолжает печатать в левой половине экрана, оставляя правую половину экрана для возможной корректировки вводимой информации. При этом любой вывод в правую половину экрана возможен только прямого обращения к памяти дисплея, как объяснено в [4.3.1]. PCjr использует в Бейсике 7 номеров режимов: Номер Режим 0 текстовый режим, ширина может быть 40 или 80 1 4-цветная графика среднего разрешения 2 2-цветная графика высокого разрешения 3 16-цветная графика низкого разрешения 4 4-цветный режим среднего разрешения 5 16-цветный режим среднего разрешения 6 4-цветная режим высокого разрешения Последние четыре режима требуют дискетты с Бейсиком. Размер страницы определяет количество памяти, требуемое для одного экра- на (дисплейные страницы обсуждаются в [4.5.3]). Программа должна отвести соответствующее количество памяти перед установкой режи- ма. Это делается оператором CLEAR. За оператором CLEAR должны следовать три числа, определяющие отводимую память, третье из этих чисел устанавливает размер видеобуфера (первые два параметра обсуждаются в [1.3.1]). Например, размер для видеобуфера 16K, устанавливаемый по умолчанию, выделяется командой CLEAR ,,16384. К сожалению, размер видеобуфера указывается в байтах, поэтому он не равен круглому числу типа 4000 или 32000, а равен 4096 или 32768. Помните, что 2K = 2^11, 4K = 2^12, 16K = 2^14, а 32K = 2^15. Для выделения трех страниц по 16K, введите CLEAR ,,3*2^14. Этот оператор должен помещаться в самом начале программы, пос- кольку при использовании оператора CLEAR все переменные очищают- ся. Отметим также, что при создании нескольких страниц, страница 0 начинается с младших адресов памяти. К моменту выхода этой книги Бейсик не поддерживает дополни- тельные режимы терминала EGA. В [4.3.3] приведена подпрограмма на машинном языке, которая позволит Вам установить эти режимы. Средний уровень. Функция 0 прерывания 10H устанавливает режим дисплея. В AL должен находиться номер режима от 0 до A. Чтобы установить цвет- ной графический режим среднего разрешения надо: MOV AH,0 ;номер функции MOV AL,4 ;номер требуемого режима INT 10H ;устанавливаем режим Для определения текущего графического режима надо использовать функцию F прерывания 10H. Прерывание возвращает номер режима в AL. Оно также дает номер текущей страницы дисплея в BH и число символов в строке в AH. MOV AH,0FH ;номер функции INT 10H ;получение информации о режиме дисплея MOV MODE_NUMBER,AL ;номер режима в AL MOV NUMBER_COLS,AH ;число символов в строке в AH MOV CURRENT_PAGE,BH ;номер текущей страницы в BH MS DOS обеспечивает также Esc-последовательности для установки и сброса режимов дисплея. Для этого необходимо, чтобы Вы предва- рительно загрузили драйвер ANSI.SYS, как объяснено в приложении Д. Управляющая строка имеет вид ESC [=#h, где # - номер режима, указанный как код ASCII, а ESC обозначает один символ с кодом ASCII 27. Например: ;---в сегменте данных MED_RES_COLOR DB 27, '[=4h$' MED_RES_B&W DB 27, '[=5h$' ;---установка цветного графического режима среднего разрешения MOV AH,9 ;номер функции вывода строки LEA DX,MED_RES_COLOR ;DS:DX должны указывать на строку INT 21H ;изменение режима Низкий уровень. В данном пункте цветной адаптор, монохромный адаптор и PCjr рассматриваются отдельно, поскольку они существенно отличаются. Цветной графический адаптор имеет регистр, который устанавливает режим дисплея. Он расположен в порте с адресом 3D8H. Биты 0, 1, 2 и 4 хранят установку. Бит 0 устанавливает 40 символов в строке, когда он равен 0 и 80 - когда равен 1. Бит 1 устанавливает дисп- лей в текстовый режим, когда равен 0 и в графический, когда равен 1. Бит 2 устанавливает цветной режим, когда равен 0 и черно-бе- лый, когда равен 1. И, наконец, бит 4 устанавливает для графичес- кого режима среднее разрешение, когда равен 0 и высокое разреше- ние, когда равен 1 (бит 2 должен быть равен 1). Ниже приведены возможные комбинации: Режим биты: 5 4 3 2 1 0 0. 40*25, черно-белый, текст 1 0 1 1 0 0 1. 40*25, цветной, текст 1 0 1 0 0 0 2. 80*25, черно-белый, текст 1 0 1 1 0 1 3. 80*25, цветной, текст 1 0 1 0 0 1 4. 320*200, черно-белый, графика 0 0 1 1 1 0 5. 320*200, цветной, графика 0 0 1 0 1 0 6. 640*200, черно-белый, графика 0 1 1 1 1 0 ? ? ? ? ? текст 80*25 ? ? ? ? графика 320*200 ? ? ? черно-белый ? ? разрешение вывода ? графика 640*200 мигание Изменение этих битов не приводит к изменению режима дисплея. Нужно еще много шагов, включающих изменение параметров первых 10 регистров по адресу порта 3D5H. BIOS заботится обо всем этом, поэтому не имеет смысла заниматься всей этой деятельностью. Одна- ко иногда имеет смысл реинициализировать регистр режима в его текущем режиме, изменяя биты 3 и 5, которые на самом деле не от- вечают за установку режима. Когда бит 5 сброшен в 0, то он зап- рещает атрибут мигания символов; в этом случае, если старший бит байта атрибутов установлен, то это приводит к выводу фонового цвета высокой интенсивностью (см. пример в [4.1.3]). Бит 3 этого регистра управляет разрешением вывода. Когда он равен 0, то весь экран закрашивается в цвет рамки, но видеобуфер не очищается. Вывод мгновенно возвращается, когда значение этого бита меняется на 1. Это свойство полезно использовать для избежания интерферен- ции экрана при сдвигах [4.5.1]. Некоторые утилиты используют это свойство для того, чтобы зря не утомлять фосфорное покрытие тру- бки терминала, когда компьютер включен, но не используется. Отме- тим также, что два старших бита регистра не используются. Монохромный адаптор имеет соответствующий адрес порта 3B8H. Имеют значение только три бита. Бит 0 устанавливает высокое раз- решение, которое является единственным допустимым режимом для монохромного дисплея. Если этот бит равен 0, то компьютер перес- тает работать. Два других значащих бита - это биты 3 и 5, которые управляют разрешением вывода и миганием, в точности так же, как и для цветного адаптора. PCjr распределяет информацию, содержащуюся в одном порте для монохромног и цветного адаптора. Массив ворот дисплея имеет два регистра режима, номера 0 и 3. Для доступа к этим регистрам надо послать номер регистра в порт с адресом 3DAH, а затем записать данные по тому же адресу (чтение этого порта обеспечивает, что первая запись в него будет воспринята, как указание номера тре- буемого регистра). Вот значение битов этих регистров: Регистр 0: бит 0 1 = текст, 80*25 и режимы 5 и 6, иначе 0 0 1 = графический режим, 0 = текстовый 0 1 = запрет цветов, 0 = разрешение цветов 0 1 = разрешение вывода, 0 = запрет вывода 0 1 = 16-цветный режим, 0 = все остальные режимы Регистр 3: бит 0 всегда 0 1 1 = разрешение мигания, 0 = 16 фоновых цветов 2 всегда 0 3 1 = 2-цветная графика, 0 = все остальные режимы Как и в двух предыдущих случаях, не стоит устанавливать эти ре- гистры прямо из программы, так как нужно еще много работы для программирования микросхемы 6845. Но каждый из этих регистров содержит бит, который иногда приходится программно модифициро- вать, а поскольку эти регистры только для записи, то Вам необхо- димо понимать значение всех их битов. Эти биты - бит разрешения вывода в регистре 0 и бит разрешения мигания в регистре 3. Их действие было описано ранее и возможное их использование еще не раз будет обсуждаться в этой главе (в [4.5.1] и [4.1.3]). EGA имеет два регистра, управляющих режимом дисплея. Один имеет адрес порта 3D5H. Этот регистр не содержит ни одного бита, связанного с чем-либо другим, поэтому нет никаких причин обра- щаться к нему. Второй регистр имеет адрес порта 3C0H и содержит бит, который выбирает будет ли бит 7 байта атрибутов соответство- вать миганию или высокой интенсивности. Этот вопрос обсуждается в [4.1.3]. 4.1.3 Установка атрибутов/цветов символов. Когда дисплей установлен в текстовый режим в любой из видео систем, то каждой позиции символа на экране отводится два байта памяти. Первый байт содержит номер кода ASCII кода символа, а второй - атрибуты символа. Цветной адаптор и PCjr могут выводить в цвете, как сам символ, так и всю область, отведенную данному символу (фоновый цвет). Монохромный адаптор ограничен только черным и белым цветом, но он может генерировать подчеркнутые символы, чего не могут делать цветной адаптор и PCjr. Все три системы могут выдавать мигающие символы и негативное изображение. Все три системы могут также создавать символы с высокой интенсив- ностью, хотя для цветного адаптора и PCjr повышенная интенсив- ность символа на самом деле приводит к другому цвету (восемь основных цветов имеют версии с повышенной интенсивностью, что дает набор 16 цветов). EGA умеет делать все, что могут все ос- тальные системы и многое другое. В частности, на улучшенном дисп- лее он может выводить подчеркнутые цветные символы, поскольку матрица изображения символов 8*14 дает такую возможность. Атрибуты цвета: Для указания цветов экрана одни и те же номера кодов исполь- зуются в Бейсике и прерываниями операционной системы. Они такие: 0 - черный 8 - серый 1 - синий 9 - голубой 2 - зеленый 10 - светлозеленый 3 - циан 11 - светлый циан 4 - красный 12 - светлокрасный 5 - магента 13 - светлая магента 6 - коричневый 14 - желтый 7 - белый 15 - яркобелый Младшие четыре бита байта атрибутов устанавливают цвет самого символа (бит 3 включает высокую интенсивность). Следующие три бита устанавливают фон символа. И при обычных обстоятельствах старший бит включает и выключает мигание. Таким образом: когда бит 0 = 1, синий включается в основной цвет 1 = 1, зеленый включается в основной цвет 2 = 1, красный включается в основной цвет 3 = 1, символ выводится с высокой интенсивностью 4 = 1, синий включается в фоновый цвет 5 = 1, зеленый включается в фоновый цвет 6 = 1, красный включается в фоновый цвет 7 = 1, символы мигают Биты 0-2 и 4-6 содержат одни и те же компоненты цветов для самих символов и фона. Эти трехбитные группы позволяют 8 возмож- ных комбинаций. Когда включается бит высокой интенсивности, то добавляются еще 8 цветов. Шестнадцать возможных цветов получаются из этих установок битов следующим образом: Красный Зеленый Синий Низкая интенсивность Высокая 0 0 0 черный серый 0 0 1 синий светлосиний 0 1 0 зеленый светлозеленый 0 1 1 циан светлый циан 1 0 0 красный светлокрасный 1 0 1 магента светлая магента 1 1 0 коричневый желтый 1 1 1 белый яркобелый Можно иметь 16 цветов и для фонового цвета. В этом случае бит 7 должен служить указателем высокой интенсивности для фона, а не указателем мигания символов. Для цветного адаптора надо изменить бит 5 порта с адресом 3D8H в 0, как показано ниже. Поскольку этот порт доступен только для записи, то все остальные биты должны быть переустановлены. Эта возможность доступна только в двух случаях: текстовых режимов с 40 и с 80 символами в строке. Для режима с 80 символами надо послать в порт число 9, а для режима с 40 символами - число 8. Чтобы вернуть мигание надо добавить к обоим этим значениям 32. Для PCjr надо сбросить в 0 бит 1 регист- ра 3 массива ворот дисплея. Все остальные биты должны быть равны нулю, кроме номера 3, который должен быть установлен для режима двухцветной графики. Кроме этого режима, для установки бита мига- ния надо сначала прочитать порт с адресом 3DAH, чтобы подготовить массив ворот дисплея, затем послать в него 3, чтобы указать ре- гистр, и затем послать 0, чтобы установить бит мигания. При за- вершении программы всегда надо восстанавливать мигание, так как следующая программа может полагаться на это. EGA также может разрешать/запрещать мигание, хотя в этом слу- чае адрес порта 3C0H. Сначал надо прочитать порт 3DAH, чтобы получить доступ к адресному регистру в 3C0H. затем надо послать в 3C0H 10H, чтобы указать соответствующий регистр. Наконец, надо послать данные по тому же адресу. Поскольку этот регистр только для записи, то все биты должны быть правильно установлены. Мига- ние включается установкой бита 3, а выключается сбросом этого бита. Все остальные биты в цветном текстовом режиме должны быть равны 0. Для цветного адаптора, когда символы выводятся на дисплей в цветном графическом режиме, то они изображаются в текущем фоновом цвете. Операторы, которые выводят на экран, как в Бейсике, так и в MS DOS (прерывание 21H) ограничены выводом символов в третьем цвете используемой палетты (имеются две палетты из трех цветов - см. [4.4.1]). В палетте 0 символы желтые/коричневые, а в палетте 1 они белые. Процедуры вывода символов BIOS (прерывание 10H), однако, могут указать любой из трех цветов палетты. С другой стороны, для PCjr, цвет назначенный определенной позиции палетты может быть изменен, поэтому для вывода символов могут использова- ны любые цвета. Для PCjr цвета соответствующие данным кодовым номерам могут быть изменены. Каждый кодовый номер связан с регистром палетты в массиве ворот дисплея [4.1.1]. Эти регистры пронумерованы от 10H до 1FH, что соответствует кодам от 0 до 15. Каждый 4-битный ре- гистр содержит число в диапазоне 0-15, которое представляет реальный цвет, выводимый когда оператор программы встречает один из кодовых номеров. Например, если в каком-то месте программы указано, что символ должен выводиться с кодовым номером 0, то цвет выводимого символа определяется кодом цвета, хранящемся в регистре палетты 0. Начальное значение этого регистра 0000, поэ- тому будет выводиться черный цвет. Но содержимое этого регистра может быть изменено, скажем, на 0001, а в этом случае кодовый номер 0 приведен к выводу синим цветом. Кодовые номера, исполь- зуемые в регистрах палетты такие же, как и в операторах програм- мы. На рис. 4-1 показана начальная установка регистров палетты для всех регистров, кроме регистра для зеленого цвета, который изменен так, чтобы выводился цвет магента. Чтобы запрограммировать регистр палетты PCjr нужно сначала послать его номер (от 10H до 1FH) в массив ворот дисплея, адрес порта которого 3DAH. Затем нужно послать данные по тому же адре- су. Чтобы быть уверенным, что массив готов принять номер регист- ра, а не данные, надо сначала прочитать из порта 3DAH, отбросив прочитанное. EGA также использует 16 регистров палетты. Они расположены в порте с номером 3C0H, а номера их меняются от 00 до 0FH. Надо сначала прочитать из порта 3DAH, чтобы переключить порт на его адресный регистр, затем послать номер регистра палетты в 3C0H, а затем послать данные. Когда переключатели на EGA установлены на улучшенный режим (для улучшенного цветного дисплея IBM), то па- летта может быть выбрана из 64 цветов. В этом случае установка регистра палетты имеет длину 6 битов в формате R'G'B'RGB. Биты RGB дают темные цветы, а биты R'G'B' - цвета повышенной яркости. Когда установлены и R' и R, например, то это приводит к очень яркому красному цвету. Биты могут смешиваться давая новые оттен- ки. Если регистры палетты, предназначенные для 64 цветов, исполь- зуются не в улучшенном режиме, то 4-й и 5-й биты регистра игнори- руются и содержимое регистров рассматривается по обычной схеме RGB. Поскольку PCjr и EGA используют регистры палетты, то выбор фонового цвета не ограничен использованием бита 7 байта атрибутов в качестве бита мигания. Монохромные символы: Монохромные символы используют байт атрибутов несколько более странным образом. Как и с атрибутами цвета, биты 0-2 устанавли- вают основной цвет, а биты 4-6 - фоновый. Эти цвета могут быть только белым и черным, со следующим соответствием битам: Бит Бит Бит Основной атрибут Фоновый 6 или 2 5 или 1 4 или 0 0 0 0 черный черный 0 0 1 подчеркнутый белый белый 0 1 0 белый белый 0 1 1 белый белый 1 0 0 белый белый 1 0 1 белый белый 1 1 0 белый белый 1 1 1 белый белый Нормальный режим белый на черном, когда биты 0-2 установлены в 111, а биты 4-6 установлены в 000. Негативное изображение соз- дается обратными значениями битов. Символы выводятся с повышенной яркостью, когда бит 3 установлен в 1; не существует способа при- дать повышенную яркость фону, когда символы выводятся в негатив- ном изображении, а также недоступно подчеркивание в негативе. Во всех случаях, установка в 1 бита 7 дает мигание символов. Всего возможно только 10 комбинаций, когда символы видны. Они могут быть реализованы различными установками битов. Ниже приводятся по одной из возможных установок для каждого случая: Атрибут Цепочка битов Гекс 10-ное нормальный 00000111 7 7 интенсивный 00001111 F 15 нормальный подчеркнутый 00000001 1 1 интенсивный подчеркнутый 00001001 9 9 негативный 01110000 70 112 нормальный мигающий 10000111 87 135 интенсивный мигающий 10001111 8F 143 нормальный мигающий подч. 10000001 81 129 яркий мигающий подчерк. 10001001 89 137 яркий негативный 11110000 F0 240 Высокий уровень. Бейсик устанавливает цвета и атрибуты символов оператором COLOR. Все операторы PRINT и WRITE, которые следуют за данным оператором COLOR, выполняются с атрибутами, указанными в этом операторе. Цвет фона меняется только для выодимых символов, но не для всего экрана. Новый оператор COLOR не влияет на то, что было выведено ранее. Кроме случая монохромного адаптора, COLOR 3,4 устанавливает основной цвет символа циан (#3), а фоновый - красный (#4). Диапа- зон кодов основных цветов 0-31, причем числа 0-15 соответствуют цветам, перечисленным в вышеприведенной таблице, а числа 16-31 получаются прибавлением к любому из этих кодов числа 16, что дает тот же самый цвет, но с миганием символов. (При мигании основной цвет периодически меняется на фоновый, в то время как фоновый цвет остается неизменным.) Операторы PRINT и WRITE могут также выводить символы на графи- ческий экран. При этом цвет символов - это всегда третий цвет текущей палетты, т.е. желтый/коричневый для палетты 0 и белый - для палетты 1. Отметим, что когда Вы начинаете работать в цветном текстовом режиме, то весь экран черно-белый. Чтобы закрасить весь экран в фоновый цвет, необходимо указать оператором COLOR ,2, например, зеленый цвет и затем очистить экран командой CLS. Когда Вы чисти- те экран по ходу выполнения программы, то необходимо, чтобы пос- ледний оператор COLOR установил фоновый цвет таким, каким Вы хотите закрасить весь экран. Для монохромного дисплея атрибуты устанавливаются аналогичным образом. 0 соответствует черному цвету, а любое из чисел 1-7 соответствует белому. Таким образом COLOR 0,7 устанавливает чер- ное изображение на белом фоне (негатив), в то время как COLOR 7,0 дает вывод белых символов на черном фоне (обычная установка). Имеется одно исключение: если в качестве основного цвета исполь- зовать код 1, то будут выводиться подчеркнутые символы. Прибавив 8 к любому из кодов основного цвета, получим яркое изображение. Прибавив 16 к любому из кодов 0-15, получим мигающие символы. Таким образом 7+8+16=31 дает яркое мигающее белое изображение. Для фонового цвета допустимы только значения от 0 до 7. Если Вы используете прямое отображение в память [4.3.1], то оператор COLOR не влияет на вывод. Вместо этого Вы должны выбрать требуемую установку атрибутов из таблиц и прямо присвоить значе- ние соответствующего байта атрибутов оператором POKE. Помните, что байты атрибутов всегда занимают нечетные позиции в видеобуфе- ре. Отображение в память позволяет Вам иметь 16 фоновых цветов в Бейсике (при условии, что Вам не нужны мигающие символы). Для графического адаптора введите OUT &H3D8,8, чтобы старший бит каждого атрибута действовал как бит яркости для фоновых цветов. В следующем примере в центре экрана печатается яркокрасный "!" на светлокрасном фоне. 100 DEF SEG = &HB800 'указываем на буфер цветного дисплея 110 OUT &H3D8,8 'используем 16 фоновых цветов 120 POKE 1000,33 'печатаем ! в центре экрана 130 POKE 1001,196 'красный на светлокрасном (11000100) Как уже говорилось выше PCjr хранит бит мигания в массиве ворот дисплея. Вот та же программа для PCjr (но она не будет работать в режиме двухцветной графики): 100 DEF SEG = &HB800 'указываем на видеобуфер 110 X = INP(&H3AH) 'читаем из массива ворот дисплея 120 OUT &H3AH,3 'требуем доступ к регистру 3 130 OUT &H3AH,0 'сбрасываем все биты этого регистра 140 POKE 1000,33 'печатаем ! в центре экрана 150 POKE 1001,196 'красный на светлокрасном (11000100) Приведем еще пример изменения назначения цвета регистра палетты. Код цвета, который обычно выводится синим (0001) сделаем, чтобы он выводил цвет магента (0101). Номер регистра массива ворот дисплея, соответствующий коду цвета 1 равен 11H. 100 X = INP (&H3AH) 'читаем из массива ворот дисплея 110 OUT &H3AH,&H11 'требуем доступ к регистру 11H 120 OUT &H3AH,5 'помещаем туда код магенты (0101 = 5) Средний уровень. Прерывания DOS и BIOS предоставляют очень бедные возможности для работы с цветным текстом. Только функция 9 прерывания 10H принимает байт атрибутов при выводе символа. Функция A прерывания 10H выводит символ без указания цвета или атрибута; она просто помещает символ в видеобуфер, не трогая байт атрибута, таким образом атрибуты сохраняют свое старое значение. Функция D преры- вания 10H также оставляет нетронутым байт атрибутов. Все эти функции обсуждаются в [4.3.1]. Функции вывода на экран DOS прерывания 21H всегда выводят белое на черном. Даже если для всего экрана установлен некоторый фоновый цвет, то функции DOS устанавливают атрибут в нормальный черный при выводе каждого символа. Однако имеется способ преодо- леть это ограничение. MS DOS предоставляет драйвер устройства ANSI.SYS, который может интерпретировать специальные Esc-последо- вательности. В приложении Д объясняются основы его использования. Esc-последовательности выводятся через функцию 9 прерывания 21H, которые обычно выводят строку символов на экран. В этом случае строка состоит из символа Esc, за которым следует [, а далее одно или более кодовых чисел из нижеприведенного списка. Строка должна кончаться символом m и обычным ограничителем $. Вот кодовые номе- ра: 0 все атрибуты выключены (черный на белом) 1 включена повышенная интенсивность 4 включено подчеркивание 5 включено мигание 7 включено негативное изображение 8 все включено (при этом символы невидимы) 30 черный основной цвет 40 черный фон 31 красный основной цвет 41 красный фон 32 зеленый основной цвет 42 зеленый фон 33 желтый основной цвет 43 желтый фон 34 синий основной цвет 44 синий фон 35 основной цвет магента 45 фон магента 36 основной цвет циан 46 фон циан 37 белый основной цвет 40 белый фон Отметим, что когда функции MS DOS выводят символы в графическом режиме, то они обычно используют код 3 текущей палетты. С помощью Esc-последовательностей можно установить цвет символа соответст- вующим любому из цветов палетты. Надо указывать 30 или 31 для фонового цвета, 32 или 33 - для кода 1, 34 или 35 - для кода 2 и 36 или 37 - для кода 3. В этом случае не надо указывать фоновый цвет. В следующем примере на экран выводятся две строки с помощью функции 9 прерывания 21H. Первая выводится синим на красном, а вторая - мигающим цианом на красном. Не надо переопределять крас- ный в качестве фонового цвета для второй строки, поскольку назна- чения цветов действуют на все последующие команды вывода (включая функции BIOS прерывания 10H), до тех пор, пока не будут сделаны другие назначения. Отметим, как просто перемешивать команды уп- равления цветом с выводом самих строк. ;---в сегменте данных STRING_1 DB 'The rain in Spain',0AH,0DH,'$' STRING_2 DB 'Falls mainly on the plain$' BLUE_RED DB 27,'[34;41m$' BLINK_CYAN DB 27,'[5;36m$' ;---вывод строк MOV AH,9 ;функция вывода строки LEA DX,BLUE_RED ;адрес управляющей строки в DX INT 21H ;все будет выдаваться синим на красном LEA DX,STRING_1 ;указываем на первую строку INT 21H ;печатаем строку LEA DX,BLINK_CYAN ;адрес второй управляющей строки INT 21H ;меняем цвет на мигающий циан LEA DX,STRING_2 ;указываем на вторую строку INT 21H ;печатаем строку Вы всегда должны позаботиться о том, чтобы сбросить атрибуты цвета в нормальное состояние перед завершением программы, пос- кольку в противном случае они будут действовать и на вывод после- дующих программ. В конце следует вывести Esc-последовательность, использующую код номер 0, как указано выше. PCjr и EGA имеют специальную функцию BIOS для установки содер- жимого регистров палетты. Это подфункция 0 функции 10H прерывания 10H. Надо поместить номер регистра палетты (от 0 до 15) в BL, а значение кода цвета (также от 0 до 15) в BH, а затем выполнить прерывание. Подфункция 2 функции 10H устанавливает все регистры палетты, а также цвет границы, используя 17-байтный массив, на который должны указывать ES:DX. Байты 0-15 массива помещаются в регистры палетты 0-15, а байт 16 устанавливает цвет границы. О том, как отдельно установить цвет границы см. [4.1.4]. Низкий уровень. Как уже объяснялось в разделе "Высокий уровень", надо просто поместить требуемое значение байта атрибутов в видеобуфер, за тем символом, к которому эти атрибуты должны относиться. Приведен пример для цветного адаптора или PCjr. В примере устанавливается текстовый экран 80*25 с 16 фоновыми цветами, а затем экран ини- циализируется в красный цвет светлосинем фоне: ;---установка 16 фоновых цветов в текстовом режиме 80*25 MOV AL,00001001B ;установка в 0 бита мигания MOV DX,3D8H ;адрес регистра OUT DX,AL ;посылаем в регистр ;---инициализируем весь экран в красный на светлосинем фоне MOV AX,0B800H ;указываем на видеобуфер MOV ES,AX ; MOV CX,2000 ;записываем атрибут в 2000 ячеек MOV BX,1 ;BX указывает на байт атрибутов MOV AL,10010100B ;значение байта атрибутов NEXT_CHAR: MOV ES:[BX],AL ;посылаем атрибуты в буфер INC BX ;увеличиваем указатель на атрибуты INC BX ; LOOP NEXT_CHAR ;пишем в следующую позицию 4.1.4 Установка цвета границы экрана. Граница символьного экрана может иметь цвет, отличный от фоно- вого цвета центральной части экрана. Может быть использован любой из 16 цветов. С другой стороны, графические экраны технически не имеют области границы. Когда цвет фона устанавливается в графи- ческом режиме, то весь экран, включая область границы, окраши- вается в этот цвет. Однако, операции вывода точек на экран не имеют доступа к области границы; если большую часть адресуемых точек экрана изменить в нефоновый цвет, то будет создана види- мость границы экрана. Высокий уровень. Третий параметр оператора Бейсика COLOR устанавливает цвет границы. Используются те же самые кодовые номера цветов, приве- денные в [4.1.3]. Например, для установки границы в светлосиний цвет, надо написать COLOR ,,8. PCjr кроме того может изменять цвет, за счет изменения установки регистра палетты, соответствую- щего коду цвета, указанного для цвета границы. Полное объяснение см. в [4.1.3]. Средний уровень. Для всех видеосистем фоновый цвет может быть установлен функ- цией BH, прерывания 10H. Эта функция устанавливает также основные цвета. Чтобы указать, что надо изменить фоновый цвет, надо помес- тить 0 в BH, а код цвета в BL и выполнить прерывание. Кроме того, PCjr и EGA имеют специальную функцию для установки фонового цве- та. Это подфункция 1 функции 10H прерывания 10H. Надо поместить 10H в AH, 1 в AL и код цвета в BH. Никаких значений не возвра- щается. Низкий уровень. Для цветного графического адаптора биты 0-3 порта 3D9H (Ре- гистр выбора цвета) устанавливают цвет границы, когда экран на- ходмтся в текстовом режиме. Как обычно, назначение битов в восхо- дящем порядке - синий (B), зеленый (G), красный (R) и интенсив- ность. Поскольку этот адрес предназначен только для записи, все остальные биты этого регистра должны быть правильно установлены. Это бит 4, который, если его установить в 1, приводит к тому, что все фоновые цвета будут выводиться с высокой интенсивностью. ;---установка светлосинего цвета границы MOV AL,00001001B ;атрибут светлосинего цвета MOV DX,3D9H ;адрес регистра выбора цвета OUT DX,AL ;устанавливаем цвет границы Для PCjr массив ворот дисплея [4.1.1] имеет регистр, который устанавливает цвет границы. Это 4-битный регистр, причем биты 0-3 соответствуют синему, зеленому, красному и высокой интенсивности, когда установлены в 1. Для установки светлосинего цвета надо послать в регистр 1001. Регистр цвета границы - это регистр 2 массива ворот дисплея. Чтобы получить доступ к этому регистру надо сначала послать 2 в порт по адресу 3DAH. Затем надо послать данные по тому же адресу. Чтобы быть уверенным, что микросхема готова принять номер регистра, а не данные, надо сначала прочи- тать из порта 3DAH. Следующий пример устанавливает красный цвет границы (бит 2 установлен). MOV DX,3DAH ;адрес порта массива ворот дисплея IN AL,DX ;чтение для подготовки микросхемы MOV AL,2 ;номер требуемого регистра OUT DX,AL ;посылаем в порт MOV AL,4 ;устанавливаемс только бит 2 OUT DX,AL ;устанавливаем цвет границы Для EGA цвет границы устанавливается регистром сканирования (overscan). Это регистр номер 11H порта с адресом 3C0H. Надо сначала прочитать этот порт, чтобы переключить его на адресный регистр, затем послать туда номер 11H в качестве индекса, а затем послать данные. Имеют значение только младшие 4 бита данных, если только EGA не связан с улучшенным цветным дисплеем IBM, а в этом случае имеют значение младшие 6 битов, которые устанавливают цвет границы. 4.1.5 Очистка части/всего экрана. Очистка экрана состоит просто в записи пробела в каждую из позиций экрана (код ASCII - 32). Однако, если при выводе на экран были использованы ненормальные атрибуты, то должны быть также изменены и байты атрибутов. Операционная система обеспечивает простой способ очистки только части экрана. Высокий уровень. Бейсик для очистки экрана использует оператор CLS. При этом 25-я строка внизу экрана становится пустой только если был убран список значений функциональных клавиш с помощью команды KEY OFF. Байты атрибутов устанавливаются равными ASCII 7. В [4.5.1] дана процедура прокрутки, которая может быть использована в Бейсике для очистки окон на экране. Средний уровень. Операционная система предоставляет несколько способов очистки экрана. Какой из них Вы выберете зависит от того, какие средства требуются программе для достижения других целей. Первый метод - это просто сброс режима дисплея, используя функцию 0 прерывания 10H [4.1.2]. Для символьного экрана каждая позиция заполняется пробелом (ASCII 32), а все атрибуты устанавливаются нормальными (ASCII 7). Обычно этот метод хорош только в начале программы, когда все равно надо устанавливать режим работы дисплея. Для цветного графического адаптора и PCjr реинициализация режима дисплея приводит к катавасии на экране. Этот эффект отсутствует у монохромного адаптора и EGA. ;---очистка экрана путем установки нового режима MOV AH,0 ;номер функции установки режима дисплея MOV AL,2 ;код режима 80*25 черно-белого INT 10H ;очистка экрана Второй метод состоит в использовании функций 6 и 7 прерывания 10H, которые сдвигают экран. Число строк, на которое надо сдви- нуть экран помещается в AL и когда это число равно нулю экран очищается. Прерывание позволяет сдвигать только часть экрана, поэтому таким образом можно очистить отдельное окно на экране. Надо поместить координаты левого верхнего угла окна в CX, а коор- динаты правого нижнего угла в DX (номер строки в CH/DH, а номер столбца в CL/DL). Поместите атрибут, с которым должен чиститься экран в BH. Координаты отсчитываются от 0. ;---очистка окна между 3,4 и 13,15 MOV AH,6 ;используем процедуру сдвига MOV AL,0 ;число строк сдвига делаем равным нулю MOV BH,7 ;байт атрибутов для заполнения MOV CH,3 ;строка для верхнего левого угла MOV CL,4 ;столбец для левого верхнего угла MOV DH,13 ;строка для нижнего левого угла MOV DL,15 ;столбец для нижнего левого угла INT 10H ;чистим окно Третий метод заключается в использовании фукнции 9 прерывания 10H; которая выводит символ и атрибуты столько раз, сколько ука- зано в CX. Значение 2000 чистит весь экран, если курсор был уста- новлен в 0,0, используя метод показанный в [4.2.1]. AH должен содержать символ пробела, AL - байт атрибутов, а BH - номер стра- ницы дисплея. ;---установка курсора в левый верхний угол экрана MOV AH,2 ;функция установки курсора MOV BH,0 ;номер страницы MOV DX,0 ;координаты 0,0 INT 10H ;устанавливаем курсор ;---вывод символа пробела 2000 раз MOV AH,9 ;номер функции MOV CX,2000 ;число повторений вывода MOV AL,' ' ;символ пробела в AL MOV BL,7 ;атрибуты в BL INT 10H ;очистка экрана Наконец, DOS обеспечивает очистку экрана с помощью специальных Esc-последовательностей, которые работают с драйвером ANSI.SYS. Основные сведения о нем приведены в приложении Д. Эти последова- тельности - это строки, начинающиеся с символа Esc, а завершаю- щиеся ограничителем $. Такие строки выводятся функцией 9 прерыва- ния 21H, при этом DS:DX должны указывать на первый символ строки. DOS интерпретирует строку не выводя ее на дисплей. Чтобы стереть весь экран строка должна быть [2J. Чтобы стереть конец строки, начиная от позиции курсора (включая эту позицию), строка [K. ;---в сегменте данных CLEAR_LINE DB 27,'[K$' ;---очистка конца строки, начиная от позиции курсора MOV AH,9 ;функция вывода строки LEA DX,CLEAR_LINE ;DX должен указывать на начало строки INT 21H ;стираем конец строки Низкий уровень. На низком уровне надо просто поместить символы пробела и тре- буемый байт атрибутов в память дисплея, используя инструкцию STOSW. Вот пример для монохромного дисплея: MOV AX,0B000H ;указываем на память дисплея MOV ES,AX ; MOV DI,0 ;DI указывает на начало буфера MOV AL,32 ;символ пробела MOV AH,7 ;нормальные атрибуты MOV CX,2000 ;число повторений REP STOSW ;посылаем AX в ES:DI 2000 раз 4.1.6 Переключение между видеоадапторами. Машина может быть оснащена и монохромным и цветным адаптором, или одним из этих адапторов и EGA. Программа может выбирать, какой из мониторов должен быть активным, изменяя значения битов 4 и 5 в ячейке памяти 0000:0410. Установив оба этих бита в 1 мы выбираем монохромный адаптор. Изменив установку битов 5-4 на 10 устанавливаем графический адаптор в режиме 80 символов в строке, а на 01 - 40 символов в строке. И, наконец, изменив биты на 00, выбираем EGA. Во всех случаях Вы должны немедленно подать команду установки режима, поскольку BIOS имеет еще очень много регистров, которые надо изменить, прежде чем дисплей будет работать нормаль- но. Отметим, что хотя операционная система не может управлять одновременно двумя мониторами, программы могут осуществлять вывод на оба дисплея, используя прямое отображение в память [4.3.1] для адресов буфера неактивного монитора. Высокий уровень. В Бейсике надо просто использовать следующий код: 100 'Переключение на монохромный дисплей 110 KEY OFF: CLS 120 WIDTH 40 130 DEF SEG = 0 140 M = PEEK(&H410) 150 POKE &H410,M OR &H30 160 WIDTH 80 170 LOCATE,,1,12,13 180 KEY ON 100 'Переключение на цветной графический дисплей (80 символов) 110 KEY OFF: CLS 120 WIDTH 80 130 DEF SEG = 0 140 M = PEEK(&H410) 150 POKE &H410,(M AND &HCF) OR &H20 160 WIDTH 80 170 SCREEN 0 180 LOCATE,,1,6,7 190 KEY ON 100 'Переключение на EGA (80 символов) 110 KEY OFF: CLS 120 WIDTH 80 130 DEF SEG = 0 140 M = PEEK(&H410) 150 POKE &H410,M AND &HCF 160 WIDTH 80 170 SCREEN 0 180 LOCATE,,1,6,7 190 KEY ON Измените команды WIDTH и SCREEN, чтобы переключиться на другие начальные режимы дисплея. Низкий уровень. В ассемблере, как и в Бейсике, надо прямо изменить биты 4 и 5 по адресу 0000:0410. Надо сбросить режим дисплея сразу вслед за изменением. ;---переключение на монохромный монитор SUB AX,AX ;обнуляем AX MOV ES,AX ;устанавливаем ES на начало памяти MOV DL,ES:[410H] ;получаем байт по адресу 0000:0410 OR DL,00110000B ;устанавливаем биты 4 и 5 MOV ES:[410H],DL ;возвращаем байт MOV AH,0 ;фукция установки режима дисплея MOV AL,0 ;монохромный режим 80*25 INT 10H ;устанавливаем режим ;---переключение на цветной монитор (40 символов) SUB AX,AX ;устанавливаем ES на начало памяти MOV ES,AX ; MOV DL,ES:[410H] ;берем байт по адресу 0000:0410 AND DL,11001111B ;сбрасываем биты 4 и 5 OR DL,00010000B ;устанавливаем бит 4 MOV ES:[410H],DL ;возвращаем байт MOV AH,0 ;функция установки режима дисплея MOV AL,1 ;цветной режим 40*25 INT 10H ;устанавливаем режим ;---переключение на EGA SUB AX,AX ;устанавливаем ES на начало памяти MOV ES,AX ; MOV DL,ES:[410H] ;берем байт по адресу 0000:0410 AND DL,11001111B ;сбрасываем биты 4 и 5 MOV ES:[410H],DL ;возвращаем байт MOV AH,0 ;функция установки режима дисплея MOV AL,1 ;цветной режим 40*25 INT 10H ;устанавливаем режим Раздел 2. Управление курсором. Курсор служит двум целям. Во-первых, он служит указателем места на экране, в которое операторы программы посылают свой вывод. Во-вторых, он обеспечивает видимую точку отсчета на экране для пользователя программы. Только для второго применения курсор должен быть видимым. Когда курсор невидим (выключен), то он все равно указывает на позицию экрана. Это важно, поскольку любой вывод на экран, поддерживаемый операционной системой, начинается с текущей позиции курсора. Курсор генерируется микросхемой контроллера дисплея 6845, описанной в [4.1.1]. Эта микросхема имеет регистры, устанавливаю- щие размер и положение курсора. Микросхема 6845 делает только мерцающий курсор, хотя имеются программные способы создания не- мерцающего курсора [4.2.6]. Частота мерцания курсора не может быть изменена. В графических режимах курсор не выводится, хотя символы позиционируются на экране теми же самыми процедурами установки курсора, что и в текстовых режимах. Когда видеосистема работает в режиме, допускающем несколько дисплейных страниц, то каждая страница имеет свой собственный курсор и при переключении между страницами восстанавливается позиция курсора, которую он занимал, когда было последнее обраще- ние к восстанавливаемой странице. Некоторые режимы дисплея позво- ляют иметь до 8 дисплейных страниц и соответствующие им позиции курсора хранятся в наборе восьми 2-байтных переменных в области данных BIOS, начиная с адреса 0040:0050H. В каждой переменной младший байт содержит номер столбца, отсчитывая от 0, а старший байт содержит номер строки, также отсчитывая от 0. Когда исполь- зуется меньше чем 8 страниц, то используются переменные, располо- женные в более младших адресах памяти. 4.2.1 Установка курсора в абсолютную позицию. Для курсора могут быть установлены абсолютные координаты или координаты относительно его текущей позиции [4.2.2]. Абсолютные координаты могут меняться в пределах 25 строк и 80 (иногда 40) столбцов. Языки высокого уровня обычно отсчитывают координаты экрана, начиная с 1, и таким образом позиция левого верхнего угла 1,1. Язык ассемблера всегда начинает отсчет с нуля и позиция левого верхнего угла 0,0. Высокий уровень. Бейсик нумерует строки от 1 до 25, а столбцы от 1 до 80. Фор- мат оператора LOCATE, который устанавливает позицию курсора та- кой: LOCATE строка,столбец. Если установки курсора не делается, то он переходит в первую позицию строки после ввода возврата каретки, а сдвиг экрана начинается после того, как будет заполне- на 24-я строка. Чтобы вывести в 25-ю строку Вы должны использо- вать LOCATE (предварительно очистив эту строку с помощью KEY OFF). Для отмены автоматического сдвига экрана в строках 24 и 25 надо завершать оператор PRINT точкой с запятой (чтобы отменить сдвиг в позициях 24,80 и 25,80 надо использовать прямое отображе- ние в память [4.3.1]). Ниже приведен пример рисования вертикаль- ной черты с помощью одного из символов псевдографики в центре экрана. 100 FOR N = 1 TO 25 'повтор для каждой строки 110 LOCATE N,40 'установка курсора в середину строки 120 PRINT CHR$(186); 'печатаем вертикальную черту 130 NEXT 'переход к следующей строке Когда используется несколько дисплейных страниц, то оператор LOCATE действует на текущей активной странице памяти. Если стра- ница, выводимая в данный момент на монитор, не активна, то поло- жение курсора на экране не меняется. Отметим, что Бейсик имеет собственную переменную, хранящую текущее положение курсора. Если Вы подключите ассемблерную подпрограмму, которая изменит положе- ние курсора, то Бейсик проигнорирует новую позицию курсора, когда ему будет возвращено управление. Средний уровень. Операционная система предоставляет два способа позиционирова- ния курсора в абсолютную позицию на экране. Функция 2 прерывания 10H устанавливает курсор, относящийся к указанной странице памя- ти. Страницы нумеруются начиная с нуля и для монохромного дисплея номер страницы (находящийся в BH) должен всегда быть равным 0. DH:DL содержат строку и столбец, которые тоже нумеруются с 0. Курсор меняет свое положение на экране только если установка курсора относится к текущей активной странице. ;---установка курсора в строку 13, столбец 39 MOV AH,2 ;номер функции MOV BH,0 ;номер страницы MOV DH,13 ;строка MOV DL,39 ;столбец INT 10H ;позиционируем курсор Второй метод позиционирования курсора состоит в использовании специального драйвера устройства ANSI.SYS, который должен быть загружен при старте системы. В приложении Д даны необходимые сведения. Для вывода строки, содержащей информацию о строке и столбце используется функция 9 прерывания 21H. Строка начинается с символа Esc (ASCII 27), а завершается символом ограничителем $. Формат строки Esc[строка,столбецH$, где строка и столбец нуме- руются от нуля, а Esc обозначает код ASCII 27. Например, строка 27,'10;60H$' устанавливает курсор в строку 10, столбец 60. Хотя такой метод кажется излишне сложным, но он оказывается очень удобным при выводе ряда строк на экран, так как Esc-после- довательность обрабатывается как одна из строк набора. В данном примере три строки сообщения разбросаны по всему экрану. ;---в сегменте данных POSITION_1 DB 27,'[10;30H$' STRING_1 DB 'There are two options:$' POSITION_2 DB 27,'[13;32H$' STRING_2 DB '(1) Review part 1$' POSITION_3 DB 27,'[15;32H$' STRING_3 DB '(2) Move on to part 2$' ;---печать строк MOV AH,9 ;номер функции вывода строки LEA DX,POSITION_1 ;1-я строка позиционирования курсора INT 21H ;позиционируем курсор LEA DX,STRING_1 ;1-я текстовая строка INT 21H ;вывод строки LEA DX,POSITION_2 ;и т.д. INT 21H ; LEA DX,STRING_2 ; INT 21H ; LEA DX,POSITION_3 ; INT 21H ; LEA DX,STRING_3 ; INT 21H ; Низкий уровень. Регистры 14 и 15 микросхемы 6845 хранят положение курсора. Вы можете изменить их значение и курсор передвинется в соответствую- щую позицию экрана, но прерывания вывода на экран DOS и BIOS будут игнорировать Вашу установку и вернут курсор в старое поло- жение. Это происходит потому, что каждый раз при вызове этих прерываний, они восстанавливают регистры курсора, используя 2-байтное значение, хранящееся в области данных BIOS. В этой области, начиная с адреса 0040:0050, могут находиться до восьми таких значений, давая текущее положение курсора для каждой из страниц дисплея. Процедура низкого уровня должна модифицировать и эти значения, чтобы изменить состояние курсора полностью. Позиция курсора хранится в регистрах 14 и 15 как число от 0 до 1999, что соответствует 2000 (25*80) позициям экрана. Не спутайте эту систему нумерации с позициями видеобуфера от 0 до 3999, где каждый символ сопровождается еще байтом атрибутов (для получения эквивалентного указателя на позицию курсора надо сдвинуть указа- тель видеобуфера на 1 бит вправо). Обращаем также Ваше внимание, на то, что не надо менять местами старший и младший байты: в регистре 14 - старший, а 15 - младший. ;---в программе MOV BL,24 ;строка в BL (0-24) MOV BH,79 ;столбец в BH (0-79) CALL SET_CURSOR ;вызов процедуры ;---процедура установки курсора SET_CURSOR PROC ;получаем доступ к регистру младшего байта MOV DX,3B4H ;порт адресного регистра 6845 MOV AL,15 ;выбираем регистр 15 OUT DX,AL ;посылаем запрос ;вычисление позиции курсора MOV AL,80 ;умножаем номер строки на 80 MUL BL ;в AX - номер строки, умноженный на 80 MOV BL,BH ;переносим номер столбца в BL SUB BH,BH ;распространяем BL на BX ADD AX,BX ;вычисляем позицию курсора ;посылаем младший байт результата INC DX ;адресуем управляющий регистр OUT DX,AL ;посылаем младший байт ;получаем доступ к регистру старшего байта MOV AL,14 ;номер требуемого регистра DEC DX ;восстанавливаем порт адресного регистра OUT DX,AL ;посылаем запрос ;посылаем старший байт результата INC DX ;адресуем управляющий регистр MOV AL,AH ;помещаем старший байт в AL OUT DX,AL ;посылаем старший байт RET SET_CURSOR ENDP 4.2.2 Относительное позиционирование курсора Иногда бывает полезным сдвинуть курсор относительно его преды- дущей позиции: на строку вверх, на три столбца вправо, и т.д. Достаточно просто использовать для этой цели уже описанное абсо- лютное позиционирование курсора. Но для удобства MS DOS предос- тавляет некоторые возможности относительного перемещения курсора. Средний уровень. Функции относительного перемещения курсора выполняются Esc-последовательностями. Это строки, которые выводятся на экран с помощью функции 9 прерывания 21H. В приложении Д даны основы их использования. Такие последовательности интерпретируются MS DOS как команды перемещения курсора, а не вывод символов строки. Строка начинается с символа Esc (ASCII 27), затем идет символ [, а символ $ отмечает конец строки. Сама строка состоит из числа позиций, на которое надо сдвинуться, и кода направления. Чтобы сдвинуться на 3 позиции: вверх 3A вниз 3B вправо 3C влево 3D Числа записываются как коды ASCII. Не преобразуйте, например, 33C (33 пробела вправо) в 33,'C'; должно быть '33C'. В нижеприведен- ном примере цифры 1-8 помещаются через определенные интервалы поперек экрана, как метки столбцов данных. Промежутки между циф- рами генерируются Esc-последовательностями, которые сдвигают курсор вправо после вывода каждой цифры. ;---в сегменте данных CURSOR_RIGHT DB 27,'[9C$' ;---установка начальной позиции курсора MOV BH,0 ;ноиер страницы MOV DH,1 ;строка MOV DL,5 ;столбец MOV AH,2 ;функция установки курсора INT 10H ;установка курсора ;---вывод цифр LEA BX,CURSOR_RIGHT ;BX будет обмениваться с DX MOV CX,8 ;число цифр для вывода MOV DL,'0' ;начинаем с 0 NEXT_NUMBER: MOV AH,2 ;функция DOS для вывода символа INT 21H ;выводим символ INC DL ;переходим к следующему коду ASCII XCHG DX,BX ;помещаем указатель на строку в DX MOV AH,9 ;функция вывода строки INT 21H ;сдвигаем курсор на 9 позиций вправо XCHG DX,BX ;возвращаем в DX код ASCII LOOP NEXT_NUMBER ;переходим к следующей цифре Имеется также пара Esc-последовательностей, которые управляют переносом курсора на следующую строку при достижении им конца текущей строки. Когда устанавливается отсутствие переноса, то лишние символы при выводе отбрасываются. Строка, запрещающая перенос - Esc [=7h (или как данные, 27,'[=7h'). Для возврата к режиму автоматического переноса на следующую строку используется строка Esc [=7l (27,'[=7l'). 4.2.3 Включение и выключение курсора. Курсор генерируется микросхемой 6845. Он функционирует совер- шенно независимо от видеопамяти. Это значит, что при прямой адре- сации в память дисплея [4.3.1] программное обеспечение должно координировать перемещения курсора с вставкой нового символа в буфер. Отметим, что микросхема 6845 не может ни создавать немер- цающий курсор, ни изменить частоту его мерцания. В [4.2.6] пока- зано как сконструировать другие "искусственные" типы курсора. Высокий уровень. Интерпретатор Бейсика автоматически выключает курсор при за- пуске программы. Курсор появляется, когда используется оператор INPUT, но не в других случаях. Если Вашей программе необходим курсор, скажем для процедуры INKEY$, то он должен быть включен установкой третьего параметра оператора LOCATE в 1 (0 снова вык- лючит его). Напоминаем, что первые два параметра оператора LOCATE устанавливают строку и столбец, в которых должен выводиться кур- сор. 100 LOCATE 15,40,1 ;включить курсор, его позиция 15,40 или 100 LOCATE ,,1 ;включить курсор в текущей позиции и 100 LOCATE ,,0 ;снова выключить курсор Курсор будет оставаться при последующих появлениях оператора LOCATE без установки каждый раз третьего параметра. Однако надо отметить, что операторы INPUT и INPUT$ выключат его после их выполнения. Средний уровень. Ассемблерные программы оставляют курсор включенным, до тех пор, пока им не указано обратное. Операционная система не предос- тавляет специальных средств выключения курсора, но это легко сделать. Надо просто позиционировать курсор за пределы экрана, с помощью функции 2 прерывания 10H установить его в первую позицию 26-й строки. Помните, что координаты отсчитываются от нуля, так что этой позиции соответствуют координаты 25,0. MOV BH,0 ;номер страницы (всегда 0 для монохромного) MOV DH,25 ;строка MOV DL,0 ;столбец MOV AH,2 ;номер функции INT 10H ;устанавливаем курсор за пределы экрана Низкий уровень. Бит 6 регистра 10 микросхемы 6845 [4.1.1] выключает курсор, когда он установлен в 1, и включает его, когда сброшен в 0. Этот регистр содержит также значение "начальной строки" для курсора, которое вместе со значением "конечной строки" определяет толщину курсора [4.2.4]. Поскольку тип курсора не имеет значения, когда курсор выключен, то надо просто поместить в регистр 10 значение 32, чтобы установить бит 6. Чтобы восстановить курсор Вы должны также вернуть значение "начальной строки" курсора. Для нормаль- ного курсора это значение равно 11. Значение "конечной строки" при этих процедурах не меняется, поскольку оно хранится в другом регистре. ;---выключение курсора MOV DX,3B4H ;номер порта адресного регистра 6845 MOV AL,10 ;выбор регистра 10 OUT DX,AL ;посылаем запрос INC DX ;доступ к регистру через следующий порт MOV AL,32 ;устанавливаем бит 6 для выключения курсора OUT DX,AL ;выключаем курсор ;---обратное включение курсора MOV AL,11 ;значение "начальной строки" OUT DX,AL ;включаем курсор 4.2.4 Изменение формы курсора. Курсор может меняться по толщине от тонкой линии до максималь- ного размера, отводимого под символ. Он строится из коротких горизонтальных отрезков, верхний из которых называется "начальной строкой" курсора, а нижний - "конечной строкой". Для монохромного дисплея под каждый символ отводится 14 строк, пронумерованных от 0 до 13, начиная сверху. Промежутки между символами обеспечивают- ся двумя верхними строками и тремя нижними. Большинство символов распологаются в строках 2-10, хотя хвостики некоторых символов достигают линий 12 и 13, в то время как подчеркивание занимает одну двенадцатую строку. На 200-строчном цветном дисплее для каждого символа отводится только 8 строк, а символ рисуется в верхних семи строках. Эти 8 строк пронумерованы от 0 до 7, начиная сверху, и нормальный кур- сор формируется одной строкой 7. (Отметим, что на цветном дисплее нет подчеркивания, поскольку использование для подчеркивания строки 7 привело бы к тому, что символы сливались бы с располо- женными под ними.) Цветной дисплей высокого разрешения использует 14-строчный монохромный вариант, когда он работает в режиме высо- кого разрешения, а когда он работает в одном из цветных графичес- ких режимов, то он использует 8-строчный режим. Курсор может быть сформирован любой комбинацией прилегающих отрезков. Для монохромного дисплея он занимает все отведенное под символ место, когда "начальная строка" равна 0, а "конечная стро- ка" равна 13 (для графического дисплея надо использовать значение "конечной строки" равное 7). Если значения "начальной" и "конеч- ной" строки совпадают, то возникает однострочный курсор. Если номер "конечной строки" меньше чем "начальной" то возникает кур- сор, состоящий из двух частей, так как происходит перенос в верх- ние строки. Например, если "начальная строка" равна 12, а "конеч- ная" - 1, то сначала заполняется строка 12, затем 13, затем 0 и, наконец, 1. Курсор при этом принимает форму двух параллельных линий, указывающих верхнюю и нижнюю границы ряда, который он занимает. BIOS хранит 2-байтную переменную по адресу 0040:0060, которая содержит текущие значения "начальной" и "конечной" строк. Первый байт содержит значение "конечной строки", а второй - "начальной". Высокий уровень. В Бейсике оператор LOCATE может не только позиционировать курсор и включать или выключать его, но и управлять его формой. Парметры, устанавливающие "начальную" и "конечную" строки - это 4-е и 5-е число, следующие за словом LOCATE. Другие параметры могут быть опущены, если присутствуют разделяющие их запятые. Таким образом, чтобы создать толстый курсор, занимающий строки со 2 по 12, надо записать LOCATE ,,,2,12. Отметим, что Бейсик обычно выключает курсор, когда начинает выполнение программы. Как вклю- чить его обратно см. в [4.2.3]. Средний уровень. Функция 1 прерывания BIOS 10H устанавливает "начальную" и "конечную" строки курсора. В CH должна быть указана "начальная", а в CL - "конечная" строка. ;---установка "начальной" и "конечной" строк курсора MOV AH,1 ;номер функции MOV CH,0 ;начать курсор в верхней строке MOV CL,7 ;окончить курсор в восьмой строке INT 10H ; Низкий уровень. Регистры 10 и 11 контроллера дисплея 6845 содержат значения "начальной" и "конечной" строки, соответственно. Доступ к обоим регистрам осуществляется через порт 3B5H для монохромного адапто- ра и 3D5H - для цветного алаптора и PCjr. Предварительно надо послать номер требуемого регистра в адресный регистр, имеющий адрес порта 3B4H (см. [4.1.1]). Значения занимают младший конец каждого регистра. Однако регистр "начальной" строки (#10) битами 5 и 6 индицирует также должен ли выводиться курсор. Поскольку курсор выводится, когда оба этих бита сброшены в 0, то просто поместив в регистр номер "начальной" строки мы установим эти биты в 0. Остальные биты этого регистра не используются. ;---установка "начальной" строки MOV DX,3B4H ;доступ к адресному регистру 6845 MOV AL,10 ;выбор регистра 6845 OUT DX,AL ;посылка запроса MOV AL,0 ;номер "начальной строки" 0 INC DX ;переходим к управляющему регистру OUT DX,AL ;посылаем номер "начальной строки" ;---установка "конечной строки" MOV AL,11 ;выбираем регистр 11 DEC DX ;возвращаемся к адресному регистру OUT DX,AL ;посылаем запрос MOV AL,7 ;номер "конечной строки" 7 INC DX ;переходим к управляющему регистру OUT DX,AL ;посылаем номер "конечной строки" 4.2.5 Чтение/сохранение/восстановление позиции курсора. Программы иногда читают и сохраняют текущее положение курсора, с тем чтобы можно было временно перевести курсор в командную строку, а затем вернуть его в исходную позицию. Текущая позиция курсора для каждой из вплоть до восьми страниц хранится в области данных BIOS. Имеется восемь 2-байтных переменных, размещающихся начиная с адреса 0040:0050. Первая позиция соответствует странице 0, вторая - странице 1 и т.д. Младший байт каждой переменной содержит номер столбца, а младший - номер строки. Как столбцы, так и строки нумеруются, начиная с нуля. Высокий уровень. В Бейсике оператор CRSLIN возвращает строку, а POS - столбец. Оператор POS должен быть снабжен фиктивным аргументом, т.е. он всегда должен записываться в виде POS(0). В данном примере курсор переводится в нижнюю строку экрана, а затем возвращается на мес- то. Отметим, что курсор возвращается на место после выполнения оператора INPUT [4.2.3]. 100 ROW = CRSLIN 'получаем строку курсора 110 COL = POS(0) 'получаем столбец курсора 120 LOCATE 25,1 'переводим курсор в командную строку 130 INPUT "Enter file name", F$ 'запрос на ввод 140 LOCATE ROW,COL,1 'восстанавливаем позицию курсора Средний уровень. Функция 3 прерывания 10H возвращает строку курсора в DH, а столбец - в DL. На входе надо поместить в BH номер страницы (всегда 0 для монохромного дисплея). ;---определение позиции курсора MOV AH,3 ;номер функции MOV BH,0 ;страница 0 INT 10H ;строка:столбец в DH:DL MS DOS предоставляет две Esc-последовательности для сохранения и восстановления позиции курсора. Это специальные строки, которые если их "вывести" на терминал управляют монитором. Основы исполь- зования этих последовательностей описаны в приложении Д. Последо- вательность для запоминания позиции курсора - Esc[s, а для восс- тановления - Esc[u. Нет нужды запоминать координаты в переменной. ;---в сегменте данных SAVE_CURSOR DB 27,'[s$' RESTORE_CURSOR DB 27,'[u$' ;---сохранение курсора LEA DX,SAVE_CURSOR ;адрес начала строки в DX MOV AH,9 ;номер функции вывода строки INT 21H ;сохраняем позицию курсора ;---восстановление курсора LEA DX,RESTORE_CURSOR ;адрес начала строки в DX MOV AH,9 ;номер функции вывода строки INT 21H ;восстанавливаем позицию курсора Низкий уровень. Регистры 14 и 15 микросхемы 6845 хранят текущую позицию курсо- ра, как объяснялось в [4.1.1]. Старший байт хранится в регистре 14. Два байта хранят числа от 0 до 1999 в режиме 80 символов в строке и от 0 до 999 в режиме 40 символов. Вам необходимо пере- вести получаемое число в координаты строки и столбца. Вы можете прочитать это значение, чтобы узнать текущее позицию видимого курсора на экране. Но запоминание этого значения и последующее восстановление его в регистрах не обязательно приведет к возврату курсора в предыдущую позицию, особенно если Ваша программа ис- пользует любую из обычных функций работы с экраном, предоставляе- мых операционной системой. Это происходит потому, что BIOS хранит положение курсора в своих переменных, для того чтобы иметь воз- можность управлять страницами дисплея [4.5.3]. После того как Вы восстановите регистры 14 и 15 курсор переместится в соответствую- щую позицию, но при следующем вызове прерывания вывода на экран курсор вернется назад к той позиции, в которой он должен нахо- диться согласно значениям переменных BIOS. 4.2.6 Создание альтернативных типов курсора. Все прерывания операционной системы, связанные с выводом на экран, используют курсор. Вы можете изменить форму курсора с помощью техники показанной в [4.2.4] или сделать курсор невидимым [4.2.3]. Возможны альтернативные типы курсора, когда вывод на экран осуществляется с помощью метода прямого отображения в па- мять [4.3.1]. При этом "истинный" курсор выключается, поскольку он не будет адресовать символы в определенную позицию видеобуфе- ра. Вместо этого создается "фальшивый" курсор с помощью байта атрибутов. Наиболее эффективным методом является установка атрибута выво- да в негативе для символа, на который указывает курсор. Для чер- но-белого экрана для этого атрибута следует использовать код ASCII 112. Другой способ - заставить символ, на который указывает курсор мигать. В этом случае надо просто добавить 128 к текущему значению атрибута, чтобы символ начал мигать, и вычесть 128, чтобы прекратить мигание. Третий способ - установить для символа режим подчеркивания (используя код ASCII 1). И, наконец, в прог- раммах использующих командную строку можно рассмотреть возмож- ность использования специального графического символа, который следует за последним символом командной строки, такого как стрел- ки выводимые кодами ASCII 17 или 27. Отметим, что когда программа получает ввод в нескольких режимах, то Вы можете помочь идентифи- цировать текущий режим за счет особого типа курсора. Высокий уровень. В данном примере курсор формируется за счет вывода символа в позиции курсора в негативе. Переменная CURSORPOSITION хранит смещение символа, на который указывает курсор в видеобуфере. Это четное число в интервале от 0 до 3998. Прибавление к этой пере- менной 1 дает позицию байта атрибутов для этого символа и помес- тив туда 112 мы обеспечим вывод этого символа в негативе. Пере- менная FORMERATTRIBUTE хранит обычные атрибуты символа, с тем чтобы можно было восстановить их после того как курсор сдвинется. 500 '''процедура анализа поступающих расширенных кодов . 560 IF EXTENDEDCODE = 77 THEN GOSUB 5000 'курсор вправо 5000 '''процедура сдвигающая курсор вправо на одну позицию 5010 POKE CURSORPOSITION+1,FORMERATTRIBUTE 'восст. атрибут 5020 CURSORPOSITION = CURSORPOSITION+2 'новая позиция 5030 FORMERATTRIBUTE = PEEK(CURSORPOSITION+1) 'сохр. атрибут 5040 POKE CURSORPOSITION+1,112 'включаем негатив 5050 RETURN 'все сделано Низкий уровень. Здесь тот же самый пример реализован на ассемблере: ;---процедура перемещения курсора на одну позицию вправо CURSOR_RIGHT: MOV BX,CURSORPOSITION ;получение позиции INC BX ;указываем на атрибут символа MOV AL,FORMERATTRIBUTE ;берем сохраненный атрибут MOV ES:[BX],AL ;восстанавливаем его INC BX ;указываем на следующий символ MOV CURSORPOSITION,BX ;сохраняем его смещение MOV AL,ES:[BX]+1 ;получаем атрибут нового символа MOV FORMERATTRIBUTE,AL ;сохраняем его MOV AL,112 ;помещаем атрибут вывода в негативе MOV ES:[BX]+1,AL ;засылаем его для следующего символа Раздел 3. Вывод символов на экран. Имеется много способов вывода символов на экран. Некоторые просто помещают один символ, белый на черном, в текущую позицию курсора. Другие методы более сложны, но дают больше возможностей управления размещением символов, а также их атрибутами и цветами. Некоторые процедуры выводят на экран целые строки. Но в любом случае, основной операцией, на которой основан вывод, является помещение кода ASCII выводимого символа в указанную позицию ви- деобуфера; при этом может также записываться и байт атрибутов в следующий адрес памяти. Ваши программы могут помещать эти коды непосредственно в бу- фер, этот метод называется отображением в память. Отображение в память, как правило, требует больше усилий при программировании для выполнения заданной функции, чем при использовании процедур операционной системы, но в результате получаем более быстрый вывод на экран. IBM не рекомендует использовать этот метод вывода на экран, поскольку будущие изменения аппаратуры могут привести к тому, что программы будут работать неверно. Но на самом деле пока все новые разработки IBM следуют одной и той же схеме адресации, на которой основано отображение в память. 4.3.1 Вывод на экран одного символа. Все процедуры для вывода символа на экран в BIOS и DOS (а также в Бейсике) помещают символ в текущую позицию курсора и автоматически передвигают курсор на одну позицию вправо. Все они переносят вывод на следующую строку при достижении конца строки, если не сделано специальных указаний отбрасывать все символы за 80-м столбцом [4.2.2]. Важное отличие между отдельными процедура- ми состоит в том, что некоторые вместе с символом пишут также и его атрибуты, а некоторые этого не делают. Как в языках высокого, так и в языках низкого уровня, символы могут выводиться на экран без использования обычных операций печати. Вместо этого используется прямое отображение в память, при котором коды символов и их атрибуты прямо засылаются в ячейки памяти видеобуфера, соответствующие определенной позиции курсора на экране. Буфер начинается с адреса B000:0000 для монохромного адаптора и с адреса B800:0000 - для цветного графического адапто- ра и PCjr. EGA использует те же самые адреса в аналогичных режи- мах экрана. Позиции с четными номерами (начиная с нуля) содержат коды ASCII символов, а позиции с нечетными номерами - байты атри- бутов. На рис. 4-2 показан участок памяти видеобуфера. При этих операциях позиция курсора не меняется и он может быть выключен при желании [4.2.3]. Вместо курсора надо хранить переменные, служащие указателями на текущую позицию. Высокий уровень. Бейсик выводит как отдельные символы, так и целые строки, с помощью одних и тех же операторов PRINT и WRITE. Как правило, используется PRINT; WRITE - это один из вариантов со специальны- ми, редко используемыми форматами вывода. PRINT работает с данны- ми трех видов. Он выводит содержимое как строковых, так и число- вых переменных, например, PRINT S$ или PRINT X. Он выводит также символы, вставленные (в кавычках) внутрь самого оператора PRINT, например, PRINT "This words are printed". Он выводит также симво- лы, соответствующие кодам ASCII, включенным в оператор PRINT в виде операторов CHR$, например, PRINT CHR$(65), что приводит к выводу на экран символа A (код ASCII #65). В одном операторе PRINT могут выводиться много данных, при этом все три формы данных могут быть перемешаны. Отдельные данные отделяются запятой или точкой с запятой. Запятая приводит к тому, что следующие данные будут выводиться со следующей позиции табу- ляции данной строки. Точка с запятой приводит к тому, что данные печатаются на экране подряд, не разделенные пробелами (отметим, что PRINT вставляет пробел перед выводом любой числовой перемен- ной, а WRITE не делает этого). Обычно оператор PRINT автоматичес- ки делает перевод на новую строку при завершении, таким образом следующий такой оператор начнет вывод с новой строки экрана. Чтобы перенос на новую строку не происходил надо в конце операто- ра PRINT поставить точку с запятой, например, PRINT S$;. Для установки позиции курсора перед выводом используется оп- ератор LOCATE. Без оператора LOCATE PRINT всегда начинает вывод с первой позиции строки, в которой находится курсор. Последователь- ные операторы PRINT заполняют экран до тех пор, пока не будет записана 24-я строка, после чего экран сдвигается вверх, с тем чтобы следующий оператор PRINT снова выводил 24-ю строку. PRINT может выводить в 25-й строке только при помощи LOCATE; и это также приводит к автоматическому сдвигу экрана вверх. Чтобы зап- ретить сдвиг надо окончить оператор PRINT точкой с запятой. Одна- ко этот метод не сработает в последних позициях строк 24 и 25. Для заполнения этих позиций без сдвига экрана Вы должны использо- вать отображение в память, как показано ниже. Вы можете включать управляющие символы [7.1.9] внутрь операто- ра PRINT для того чтобы реализовать перемещения курсора внутри строки. Например, если Вы поместите в строку CHR$(13), то в этой точке будет сделан возврат каретки. Если Вы выведете оператором PRINT строку "One"+CHR$(13)+"Two"+CHR$(13)+"Three", то в резуль- тате каждое слово будет выводиться с новой строки. Коды ASCII 28-31 сдвигают курсор на одну позицию соответственно вправо, влево, вверх и вниз. Оператор PRINT не содержащий данных приводит к выводу возврата каретки и, таким образом, следующий оператор PRINT будет выводить на строке через одну. Прямое отображение в память существенно увеличивает скорость вывода на экран в Бейсике. Оно особенно полезно при конструирова- нии табличного вывода, когда формы могут достигать правого нижне- го угла экрана. Сначала надо установить указатель сегмента на &HB000, а затем использовать оператор POKE для засылки байтов памяти. Прилегающие по горизонтали символы отстоят друг от друга на два байта, разделяемые байтом атрибутов. Для 80-символьных экранов прилегающие по вертикали символы отстоят на 160 байт друг от друга (2 байта для каждого символа и атрибутов). В следующих двух примерах вдоль границы экрана рисуется рамка, используя символы псевдографики. В первом примере чаще используется опера- тор PRINT, а во втором используется исключительно прямое отобра- жение в память. Отметим, что и в первом случае приходится исполь- зовать прямое отображение в память в последних столбцах строк 24 и 25, чтобы избежать сдвига экрана. Использование PRINT: 10 CLS: KEY OFF 'очистка экрана 20 DEF SEG = &HB000 'указываем на видеобуфер 30 LOCATE 1,1: PRINT CHR$(201) 'левый верхний угол 40 LOCATE 1,80: PRINT CHR$(187) 'правый верхний угол 50 LOCATE 1,24: PRINT CHR$(186) ' 60 LOCATE 1,25: PRINT CHR$(200) ' 70 POKE 3838,186 'позиция 80 строки 24 80 POKE 3998,188 'позиция 80 строки 25 90 FOR N=2 TO 79 'горизонтальные линии 100 LOCATE 1,N: PRINT CHR$(205);: LOCATE 25,N: PRINT CHR$(205) 110 NEXT ' 120 FOR N=2 TO 23 'вертикальные линии 130 LOCATE N,1: PRINT CHR$(186): LOCATE N,80: PRINT CHR$(186) 140 NEXT Использование прямого отображения в память: 10 CLS: KEY OFF 'очистка экрана 20 DEF SEG = &HB000 'буфер монохромного дисплея 30 POKE 0,201 'левый верхний угол 40 POKE 158,187 'правый верхний угол 50 POKE 3840,200 'левый нижний угол 60 POKE 3998,188 'правый нижний угол 70 FOR N=2 TO 156 STEP 2 'горизонтальные прямые 80 POKE N,205: POKE N+3840,205 'как верхняя, так и нижняя 90 NEXT 100 FOR N=160 TO 3680 STEP 160 'вертикальные прямые 110 POKE N,186: POKE N+158,186 'правая и левая 120 NEXT Средний уровень. Операционная система предоставляет шесть процедур вывода на экран - три в BIOS и три в DOS. Они отличаются главным образом тем, передвигается курсор или нет, после вывода символа, вызывают ли они сдвиг экрана, позволяют ли они устанавливать атрибуты и цвета символов, а также какие управляющие коды они интерпретируют (некоторые рассматривают символ BackSpace, просто как обычный символ, а некоторые действительно сдвигают курсор на одну позицию назад). Эти шесть процедур следующие: Прерывание 10H: функция 9 вывод символа с атрибутами A вывод символа без атрибутов E "телетайпная" процедура (как на принтер) Прерывание 21H: функция 2 вывод символа без атрибутов 6 вывод символа без атрибутов 9 вывод строки символов Функции 9 и A прерывания 10H вообще не интерпретируют управ- ляющие символы. Функции DOS интерпретируют управляющие коды, приведенные в следующей таблице. Функция E прерывания 10H интерп- ретирует все коды таблицы, кроме ASCII 9. ASCII 7 звонок ASCII 8 возврат на шаг (BackSpace) ASCII 9 табуляция ASCII 10 перевод строки ASCII 13 возврат каретки Первые две функции прерывания 10H не передвигают курсор после вывода символа. Функция 9 этого прерывания выводит на экран с указанием атрибутов, а функция A - без указания, при этом сохра- няется текущее значение байта атрибутов для этого символа. AL должен содержать выводимый символ, а BL - атрибуты. Номер страни- цы дисплея содержится в BH. Он должен указываться даже для монох- ромного дисплея, который имеет только одну страницу памяти дисп- лея. В этом случае должна быть установлена первая страница, кото- рой соответствует номер 0. Особое свойство этих двух функций BIOS состоит в том, что символ выводится такое число раз, какое указа- но в CX. Обычно указывают CX равным 1, но эти функции могут легко выводить целые строки символов, если указать большее значение счетчика - полезное свойство при создании рамок. Отметим, что даже если выводится много символов, то позиция курсора не изме- няется. Когда строка выводимых символов займет все свободное пространство экрана справа-вниз от курсора, то вывод будет пере- несен в первые позиции экрана. ;---вывод символа в негативе MOV AH,9 ;функция записи с атрибутами MOV AL,THE_CHARACTER ;символ в AL MOV BL,112 ;атрибуты в BL MOV BH,0 ;страница 1 MOV CX,1 ;вывести один раз INT 10H Вместо того, чтобы постоянно восстанавливать значение счетчика в CX прерывание BIOS предоставляет также телетайпную процедуру, которая больше подходит для вывода строки символов. Она выпол- няется функцией E. Она готовится так же, как и функция A, но не надо засылать значение в CX. Строка выводится просто за счет изменения символа в AL и повторного вызова прерывания. При ис- пользовании в графическом режиме в BL устанавливается цвет палет- ты, в противном случае сохраняется старый атрибут. ;---вывод строки с помощью телетайпной процедуры MOV AH,0EH ;номер функции MOV BH,0 ;номер страницы LEA BX,STRING ;BX указывает на строку NEXT_CHAR: MOV AL,[BX] ;берем символ в AL CMP AL,'$' ;проверка на конец строки JE ALL_DONE ;если да, то выход INT 10H ;вывод строки INC BX ;переходим к следующему символу JMP SHORT NEXT_CHAR ;повторяем процедуру ALL_DONE: Прерывание DOS 21H как правило предоставляет более полезные процедуры, поскольку они перемещают курсор и приводят к сдвигу экрана при достижении нижней строки, а также интерпретируют неко- торые из обычных управляющих кодов. Функции DOS выводят на стра- ницу, которая должна быть установлена функцией 5 прерывания 10H [4.5.3]. Предоставляются две функции для вывода символа, с номе- рами 2 и 6. Первая из них распознает Ctrl-Break [3.2.8], а вторая - нет. (Когда с клавиатуры вводится Ctrl-Break, то процедура обработки Ctrl-Break не выполняется до тех пор, пока не исполь- зуется функция, которая распознает его наличие). Обе функции выводят белые символы на черном фоне, до тех пор, пока не сделана специальная установка цвета с помощью драйвера устройства ANSI.SYS [4.1.3]. В общем необходимо только поместить символ в DL, номер функции в AH и вызвать прерывание 21H. Однако функция 6 особенная в том смысле, что она имеет второе назначение в качестве функции ввода с клавиатуры. Она выступает в этой роли только если в DL помещен код FF [3.1.5]. Во всех остальных слу- чаях она выводит на экран содержимое DL. В следующем примере функция 6 поочередно принимает и печатает символ (в [3.1.4] об- суждается процедура, которая комбинирует оба этих свойства). MOV AH,6 ;номер функции NEXT: MOV DL,0FFH ;при этом значении принимаем ввод INT 21H ;выполняем прерывание JZ NEXT ;если не было ввода, то обратно CMP AL,13 ;это был возврат каретки? JE END_INPUT ;если да, то на конец MOV DL,AL ;иначе посылаем символ в DL INT 21H ;и выводим его на экран JMP SHORT NEXT ;повторяем процедуру Низкий уровень. На нижнем уровне весь вывод на экран осуществляется через отображение в память. Эту технику не рекомендуют использовать, чтобы не столкнуться с проблемой совместимости с будущими поколе- ниями машин, однако до сих пор IBM делало видеобуфер своих микро- компьютеров устроенным одинаково и расположенным в одних и тех же адресах памяти. Поскольку буфер устроен таким образом, что байты атрибутов перемежаются с байтами символов, то символьные данные не могут просто пересылаться из памяти в буфер инструкцией MOVSB, поскольку указатель в буфере должен увеличиваться на два после каждого переноса байта. Однако, использование этой техники су- щественно ускоряет вывод на экран. Отметим, что отображение в память не работает при выводе символов в графическом режиме. В этом случае размер видеобуфера 16K или 32K и BIOS рисует каждый символ поточечно. Отметим также, что при отображении в память не используется курсор для указания на символ. При желании можно перемещать курсор по мере ввода [4.2.1] или выключить его и соз- дать свой псевдокурсор [4.2.6]. ;---в сегменте данных SAMPLE_STRING DB 'PRINT THIS STRING$' ;---вывод строки MOV AX,0B000H ;монохромный дисплей MOV ES,AX ;указываем на видеобуфер LEA BX,SAMPLE_STRING ;BX указывает на строку MOV DI,CURSOR_START ;начальная позиция в буфере NEXT: MOV AL,[BX] ;берем символ CMP AL,'$' ;проверка на конец строки JE ALL_DONE ;если да, то выход MOV ES:[DI],AL ;иначе помещаем символ в буфер INC DI ;увеличиваем указатель на 2 INC DI ; INC BX ;переходим к обработке следу- JMP SHORT NEXT ;щего символа ALL_DONE: У цветного графического адаптора и PCjr (но не у EGA) имеется проблема, связанная с отображением в память. Когда запись в бу- ферную память происходит одновременно с чтением ее для вывода на экран, то на экране возникает интерференция. Эта проблема решает- ся ожиданием сигнала "все чисто" (all clear) перед записью в видеобуфер. Надо непрерывно читать значение из порта 3DAH. Когда бит 0 равен 1, то можно спокойно писать. (3DAH - это порт, через который PCjr посылает данные массиву ворот дисплея; когда из него читаем, то он возвращает регистр статуса, как и у цветного адап- тора.) ;---ожидаем пока все чисто MOV DX,3DAH ;порт регистра статуса CHECK_AGAIN: IN AL,DX ;получаем значение TEST AL,1 ;проверка первого бита JNE CHECK_AGAIN ;если он 0, то обратно ;---теперь выводим сообщение LEA BX,MESSAGE ;сообщение в сегменте данных MOV DI,2000 ;начинаем вывод с центра экрана MOV AH,01000001B ;атрибут синий на красном NEXT_CHAR: MOV AL,[BX] ;берем символ CMP AL,'$' ;проверяем на конец строки JE ALL_DONE ;если конец, то на выход MOV ES:[DI],AX ;иначе выводим символ INC BX ;увеличиваем указатель строки INC DI ;увеличиваем указатель буфера INC DI ; JMP SHORT NEXT_CHAR ;обрабатываем следующий символ ALL_DONE: Вы можете поэкспериментировать сколько символов за один цикл может выводить Ваша процедура без появления интерференции. Имейте ввиду, что при первом выполнении цикла тестируемый бит может быть равным единице, но может не оставаться времени, чтобы завершить операцию записи. PCjr специально сконструирован таким образом, что вывод в адреса, используемые буфером цветного графического дисплея пере- направляется в ту область памяти, где на самом деле находится буфер. Это свойство позволяет делать программное обеспечение, подходящее для обоих систем. 4.3.2 Вывод строки символов на экран. Процедуры, которые выводят целые строки символов очень полез- ны, но они могут накладывать ограничения на содержимое выводимой строки. Надо обращать внимание на то, какие управляющие коды (табуляция, пробел и т.п.) интерпретируются, а какие нет. До появления AT BIOS не имел функции вывода строки, хотя MS DOS всегда имела такую фукнцию. Функция BIOS предоставляет больший контроль над атрибутами символов. Естественно, что ее использова- ние создает проблему совместимости с предыдущими машинами. Напо- минаем, что EGA имеет ПЗУ, расширяющее ROM-BIOS и функция вывода строки символов является одним из таких расширений. В этом случае любой IBM PC и XT имеет возможность использовать эту процедуру. Высокий уровень. Бейсик выводит строку точно так же, как и отдельные символы. Надо просто написать PRINT S$, где S$ может быть любой строкой длиной до 255 символов, которую сконструировала программа. Ин- терпретируются 10 управляющих кодов, а именно: ASCII 7 звонок ASCII 9 табуляция ASCII 10 перевод строки ASCII 11 курсор в первую позицию экрана (Home) ASCII 12 перевод формата (стирает экран + Home) ASCII 13 возврат каретки ASCII 28 курсор вправо ASCII 29 курсор влево ASCII 30 курсор вверх ASCII 31 курсор вниз Все остальные коды выводятся на экран как символы. Средний уровень. Функция 9 прерывания 21H выводит строку. DS:DX должны указы- вать на первый символ строки. Строка должна завершаться символом $, что означает, что сам символ $ не может входить в строку. Строка может быть любой длины. Функция не переводит автоматически курсор на начало следующей строки после завершения вывода; чтобы это выполнялось надо добавить в конец строки символы 0AH (перевод строки) и 0DH (возврат каретки). ;---в сегменте данных FIRST_STRING DB 'This is the first string',0AH,0DH,'$' SECOND_STRING DB 'And this is the second string$' ;---вывод строки MOV AH,9 ;номер функции вывода строки LEA DX,FIRST_STRING ;загружаем адрес первой строки INT 21H ;печатаем строку с позиции курсора LEA DX,SECOND_STRING ;загружаем адрес второй строки INT 21H ;печатаем строку с начала новой строки Интрепретируются следующие управляющие коды: ASCII 7 звонок ASCII 8 возврат на шаг (BackSpace) ASCII 9 табуляция ASCII 10 перевод строки ASCII 13 возврат каретки Функция DOS 40H прерывания 21H также полезна при выводе строк на экран. Она требует, чтобы Вы знали длину строки, поскольку ей не требуется символа-ограничителя; эта функция особенно удобна для дампа текстовых файлов на экран. Исходно эта функция была предназначена для вывода в файл. Она требует дескриптора, который является идентификационным номером для данного файла или уст- ройства. Дисплей имеет заранее предназначенный дескриптор #1. Надо поместить дескриптор в BX, а число байтов строки в CX. DS:DX должны указывать на строку. Функция выводит текст с нормальными (белый на черном) атрибутами. Отметим, что не надо предварительно "открывать" дисплей, как это Вы делает с другими файлами при использовании этой функции. Вот пример: ;---вывод 1000 байтов текста MOV AH,40H ;номер функции MOV BX,1 ;дескриптор дисплея LEA DX,STRING ;загржаем адрес строки MOV CX,1000 ;число выводимых байтов INT 21H ; MS DOS предоставляет набор Esc-последовательностей, которые являются специальными управляющими строками для аппаратуры. Когда они выводятся с помощью функции 9 прерывания 21H, то они могут управлять курсором, режимом дисплея, цветом символов и некоторыми аспектами клавиатуры. В приложении Д обсуждается как их использо- вать. Когда программа выводит на экран много строк, то Esc-после- довательности часто являются самым удобным способом позициониро- вания курсора и установки цвета строки. Это происходит потому, что они сами рассматриваются просто как очередные строки в серии выводимых строк. У AT и машин, снабженных EGA, функция 13H прерывания 10H выво- дит строку. ES:BP должны указывать на строку, а длина строки должна быть в CX. DX указывает позицию курсора, с которой должна начинаться строка (вычисляемую как смещение от начала страницы, на которую идет вывод без учета байтов атрибутов). В BX должен быть указан номер страницы. Наконец номер кода от 0 до 3, содер- жащийся в AL указывает как должна выводиться строка. AL = 0 строка состоит только из символов, курсор неподвижен AL = 1 строка состоит только из символов, курсор движется AL = 2 в строке чередуются символы и атрибуты, курсор неподвижен AL = 3 в строке чередуются символы и атрибуты курсор движется Когда AL равно 0 или 1, то атрибуты должны находиться в BL. Все символы будут выводиться с этими атрибутами. Эта функция интерп- ретирует возврат на шаг, перевод строки, возврат каретки и звонок как управляющие команды, а не как печатаемые символы. Низкий уровень. Ограничение на использование символа $ делает функцию 9 беспо- лезной для многих приложений. Однако на многих машинах это е- динственное прерывание, доступное для вывода строки неизвестной длины. Попробуйте написать свое собственное прерывание (в [1.2.3] показано как), использующее технику отображения в память [4.3.1]. Используйте в качестве ограничителя какой-нибудь специальный символ, например, ASCII 0, вместо $. Сделайте чтобы эта процедура обрабатывала только те управляющие коды, которые нужны Вам. Такой метод будет работать намного быстрее, чем при использовании функ- ции MS DOS. 4.3.3 Чтение символа и его атрибутов в данной позиции. Обычно программа получает данные из своих переменных и поме- щает их в видеобуфер для вывода на экран. В некотором смысле программа "знает" что на экране. Но встречаются ситуации, в кото- рых сам видеобуфер используется как рабочая область (например, в графиченских программах вырезки и вставки) и текущее содержимое экрана не записано в памяти программы. В этих случаях бывает необходимо прочитать с экрана, виесто того чтобы вывести на него. Функция BIOS позволяет прочитать символ и его атрибуты в опреде- ленной позиции экрана; другой метод состоит в обращении метода прямого отображения в память дисплея [4.3.1]. Чтобы прочитать символ и атрибуты в строке 0 и столбце 39 (1,40 в Бейсике) в режиме 80 символов в строке надо сложить (0*160) плюс (39*2) и взять результат в качестве смешения в видеобуфере. В случае когда нужны смещения для различных страниц см. [4.5.3]. Имейте ввиду, что обращение метода прямого отображения в память не будет рабо- тать в случае вывода символов в графическом режиме. Высокий уровень. Бейсик использует функцию SCREEN для получения символа или атрибутов (эта функция не имеет ничего общего с оператором SCREEN устанавливающим режим дисплея). SCREEN 5,10 получает код ASCII символа, расположенного в строке 5, столбце 10 (строки и столбцы нумеруются от 1). Чтобы получить атрибуты символа надо добавить третий параметр 1, например, SCREEN 5,10,1. При использовании в графическом режиме данная функция возвращает 0, если требуемая позиция экрана не содержит (немодифицированного) символа. Атрибуты также возвращаются в виде кода от 0 до 255. Поскольку Бейсик не позволяет использования двоичных чисел, то требуются некоторые манипуляции, чтобы определить атрибуты. Основной цвет равен ATTRIBUTE MOD 16. После того как Вы выделили основной цвет, цвет фона определяется по формуле (((ATTIBUTE - FOREGROUND)/16) MOD 128). Если байт атрибутов больше 127, то включено мигание (или, при соответствующей установке, включены интенсивные цвета фона [4.1.3]). В приложении Б обсуждаются битовые операции в Бейсике. Средний уровень. Функция 8 прерывания 10H возвращает символ и его атрибуты для текущей позиции курсора. В BH должен содержаться номер текущей страницы дисплея (отсчитываемый от 0 и всегда равный 0 для монох- ромного дисплея). Код символа возвращается в AL, а байт атрибутов в AH. Эта функция настолько мощная, что способна даже читать символы в графическом режиме, сообщая цвет палетты в AH. Она работает даже для символов определяемых пользователем [4.3.4]. В примере определяется символ и атрибуты в позиции 0,39 для страни- цы 2 графического адаптора: ;---установка позиции курсора MOV AH,2 ;функция установки курсора MOV DH,0 ;номер строки MOV DL,39 ;номер столбца MOV BH,0 ;номер страницы INT 10H ;позиционируем курсор ;---чтение символа и атрибутов MOV AH,8 ;функция чтения символа/атрибутов MOV BH,2 ;номер страницы INT 10H ;в AH:AL теперь атрибуты и символ Низкий уровень. Надо вычислить смещение и проделать операцию обратную прямой записи в память. При необходимости надо добавить смещение для данной страницы. В примере получаем символ и атрибуты в позиции 7,39 страницы 2 графического адаптора: ;---чтение символа и атрибутов позиции 7,39 страницы 2 MOV AX,0B800H ;адрес видеобуфера MOV ES,AX ;ES указывает на первый байт буфера MOV DI,1000H ;смещение до начала страницы MOV AL,80 ;умножаем номер строки на 160 MOV BL,7 ;номер строки MUL BL ;теперь в AX (строка-1)*160 MOV AX,39 ;номер столбца ADD BX,AX ;номер позиции в видеобуфере SHL BX,1 ;умножаем его на два MOV AX,ES:[BX][DI] ;теперь AH:AL содержат атрибуты/символ 4.3.4 Создание специальных символов. Только монохромный адаптор не может выводить символы вида, заданного самим программистом. Цветной адаптор позволяет 128 символов, определяемых пользователем, PCjr - 256, а EGA - 1024 из которых одновременно доступно 512. Для цветного адаптора ROM-BIOS содержит данные для разрисовки только первых 128 символов набора ASCII (с номерами от 0 до 127). Следующие 128 символов недоступны для Вас, пока Вы не создатите их, используя описанную здесь тех- нику. Отметим, что MS DOS 3.00 предоставляет команду GRAFTABL, которая предоставляет требуемые данные для второй порции из 128 символов. PCjr имеет данные для второй порции из 128 символов уже готовые. EGA имеет полные наборы символов для режимов с 200 стро- ками и с 350 строками. Символы для графического адаптора и PCjr описываются с помощью матрицы 8*8 точек. Данные для каждого символа содержатся в восьми байтах. Каждый байт содержит установку для точек одного ряда, начиная с верхнего ряда, причем старший бит (номер 7) соответст- вует самой левой точке в ряду. Когда соответствующий бит равен 1, то точка высвечивается. Для описания символа Вы должны определить правильные последовательности битов для восьми байтов и поместить их в последовательные ячейки памяти. На рис. 4-3 показано как 8 байтов описывают бубновую масть. Все 128 символов вместе требуют 1024 байта, хотя вовсе не требуется, чтобы были описаны все символы. Специальный вектор прерывания (постоянный указатель в младших адресах памяти [1.2.0]) указывает на адрес первого байта первого символа расши- ренного набора, т.е. на символ номер 128. Когда в позицию символа в видеобуфере посылается код 128, то просматриваются и выводятся первые восемь байт. Если номер символа 129, то выводятся байты с девятого по шестнадцатый, и т.д. Номер этого вектора прерывания 1FH и он расположен по адресу 0000:007C. Поместите значение смещения в младшее слово (сначала младший байт), а адрес сегмента - в старшее слово (снова, сначала младший байт). Отметим, что можно символы с большими номерами кодов, не отводя памяти для символов с меньшими номерами; надо просто чтобы вектор указывал на некоторый адрес, который меньше, чем адрес начала блока, содержащего данные для описания символов. Восьмибайтные последовательности, описывающие символы ASCII с кодами 128-255 приведены в [4.3.5]. У PCjr вектор 1FH указывает на вторые 128 символов ASCII, а вектор 44H - на первые. Оба этих вектора могут быть изменены, допуская полный набор 256 символов, определяемых пользователем. Для EGA картина намного сложнее, но и намного гибче. При ини- циализации текстового режима один из двух наборов символов (8*8 или 8*14) копируется из ПЗУ EGA в карту битов 2 видеобуфера. Эта часть буфера рассматривается как разбитая на блоки, причем стан- дартный набор символов помещается в блок 0. При условии, что EGA оснащен достаточной памятью могут быть определены еще три блока для описания символов. Размер блока определяется числом строк матрицы, используемой для описания символа. Символы, описываемые матрицей 8*8 требуют 8*256 или 2048 байт. Когда разрешены более одного блока символов, то бит 3 байта атрибутов определяет из какого блока будут браться данные для описания символа. Какой из блоков будет использоваться зависит от установки битов 0-3 регистра выбора карты символов, адрес порта которого 3C5H. Предварительно надо послать 3 в порт 3C4H, чтобы указать требуемый регистр. Биты 1-0 дают номер блока символов, который берется когда бит 3 байта атрибутов равен 0, а биты 3-2 - делают то же самое, когда бит 3 равен 1. Когда установка обоих пар битов совпадает, то возможность использования двух наборов символов отсутствует и бит 3 байта атрибутов переключается на установку интенсивности символа. В этом случае используется только блок 0. Однако никто не может помешать Вам поместить свои символы в любую нужную Вам позицию в этом блоке. Если Вы изменили стандартный набор символов, то Вы можете в любой момент восстановить его из ПЗУ. Высокий уровень. В Бейсике Вы должны позаботиться о том, чтобы данные описываю- щие символы находились за пределами памяти, используемой програм- мой. Если имеется много памяти, то можно поместить данные в стар- шие адреса; если имеется опасность конфликта, то следует исполь- зовать команду CLEAR для ограничения количества памяти, которую может использовать Бейсик. Затем следует поместить адрес первого байта данных в вектор прерывания. В следующем примере описывается символ 128 как квадратная рамка. Операторы DATA содержат значе- ния, описывающие символ. Они равны либо 255, либо 129; в первом случае все биты равны 1, а во втором равны 1 только крайние биты. О вычислении десятичных значений, соответствующих данным цепочкам битов см. приложение Б. 100 '''помещаем данные, начиная с адреса &H3000 110 DATA 255, 129, 129, 129, 129, 129, 129, 255 120 DEF SEG = &H3000 'указываем начало сегмента 130 FOR N = 0 TO 7 'определяем 8 байт 140 READ Q 'читаем 1 байт 150 POKE N,Q 'помещаем его в память 160 NEXT 'и т.д. 170 '''установка вектора прерывания 180 DEF SEG = 0 'указываем на начало памяти 190 POKE 124,0 'указываем смещение 200 POKE 125,0 ' 210 POKE 126,0 'указываем сегмент 220 POKE 127,&H30 ' 230 '''печатаем символ 240 LOCATE 12,12: PRINT CHR$(128) 'теперь есть символ 128 Средний уровень. Для цветного адаптора и PCjr используйте функцию 25H прерыва- ния 21H для изменения вектора прерывания 1FH. При входе DS:DX должны указывать на первый байт блока данных. Более подробное описание см. в [1.2.3]. В примере создаются два символа с номера- ми 128 и 129. Они являются зеркальными отображениями друг друга, а выведенные подряд образуют небольшой прямоугольник. ;---в сегменте данных CHARACTER_DATA DB 11111111B, 10000000B, 10000000B, 10000000B DB 10000000B, 10000000B, 10000000B, 11111111B DB 11111111B, 00000001B, 00000001B, 00000001B DB 00000001B, 00000001B, 00000001B, 11111111B ;---установка вектора прерывания PUSH DS ;сохраняем DS LEA DX,CHAR_DATA ;смещение для данных в DX MOV AX,SEG CHAR_DATA ;сегмент для данных в DS MOV DS,AX ; MOV AH,25H ;фун