© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
(«Радиомир. Ваш
компьютер» 12/2002)
Дата последнего редактирования: 22.01.2003.
Допустим, ваша
К чему это может привести? В лучшем случае вместо нормального изображения на экране появятся «полосы». Не видя изображения, вы можете и не выйти из программы, придётся перезагружаться, что само по себе достаточно неприятно. В худшем случае ваш монитор может выйти из строя. Поэтому включение таких графических режимов, конечно, хорошо бы запретить.
Работа с дополнительными графическими режимами происходит с использованием функций, определённых стандартом VESA. Для решения поставленной задачи нам будут нужны только три функции. С помощью одной из них можно получить список номеров дополнительных режимов, с помощью другой получить по номеру режима информацию о нём (доступен или нет, разрешение по горизонтали и вертикали ), и с помощью ещё одной функции включить режим с указанным номером.
Напишем резидентную программу, которая будет перехватывать обращение к функции получения информации о конкретном режиме. (Далее я буду называть эту программу резидентом, а ту программу, которой надо запретить включение определённых графических режимов, я буду называть просто программой.) Когда программа обратится к функции получения информации о конкретном режиме, управление будет передано резиденту, который вызовет оригинальный обработчик функции, а затем проверит полученную информацию о режиме: не является ли этот режим «опасным», т.е. с разрешением больше указанного? Если является, то резидент установит в возвращаемой структуре данных признак «режим недоступен». Таким образом, программа посчитает этот режим недоступным и не будет пытаться его включить.
А если будет? А если программа вообще не делает
Чтобы резидент мог определить, является ли включаемый режим
«опасным» (не обращаясь для этого к функции получения информации о
режиме: для неё нужен
Теперь приведу информацию по используемым функциям VESA только ту, которая необходима для понимания логики работы описываемой резидентной программы. Если вас интересует полная информация, можете ознакомиться с [1] или посетить www.vesa.org.
Обращение к функциям VESA производится с помощью прерывания INT 10h. При этом в AH должно быть число 4Fh, а в AL номер функции. После обращения к функции в AL должно быть 4Fh, иначе стандарт VESA не поддерживается.
Функция 0 получение информации о версии VESA, производителе, о
списке режимов и т.п. На входе в ES:DI надо указать адрес
Функция 1 получение информации о режиме по его номеру. На входе
в CX задаётся номер режима (то самое число из списка), а в ES:DI указывается
адрес
Функция 2 включение режима. На входе в младших 9 битах регистра BX указывается номер режима, а в старших разрядах некоторые флаги, которые я здесь рассматривать не буду. Если запрашиваемого режима нет, на выходе в AX будет 1.
Ниже приведён исходный текст резидентной программы с подробными
комментариями. Программа предназначена для компиляции в
Max_X_Res equ 640 ;Максимальное разрешение по горизонтали.
Max_Y_Res equ 480 ;Максимальное разрешение по вертикали.
codesg segment
assume cs:codesg
assume ds:codesg
assume es:codesg
org 100h
begin: jmp init ;Переход к инициализации.
;Новый обработчик прерывания 10h.
new_10h: cmp ax,4f01h ;Проверка номера вызванной функции.
jz new_4f01
cmp ax,4f02h
jz new_4f02
;Вызвана не перехватываемая функция - передаём управление
;на старый обработчик:
goto_old: db 0eah ;Код межсегментного JMP.
old_10h dd ? ;Адрес старого обработчика.
;Обработка функции 1 (получение сведений о режиме):
new_4f01: push ds
push cs ;Вызываем
pop ds ;старый
pushf ;обработчик.
call old_10h
;По адресу ES:DI сформирована структура данных с информацией о режиме.
;Теперь, если горизонтальное (ES:DI+18) или вертикальное (ES:DI+20)
;разрешение режима больше максимального, сбрасываем младший бит первого
;слова структуры данных, тем самым объявляя режим недоступным.
push es
pop ds
cmp word ptr [di+18],Max_X_Res
ja main_2
cmp word ptr [di+20],Max_Y_Res
jbe main_3
main_2: and byte ptr [di],0feh
main_3: pop ds
iret
;Обработка функции 2 (установка режима):
new_4f02: and bh,1 ;Проверка: это VESA-режим?
jz goto_old ;Если нет - переход на старый обработчик.
;Проверка: номер режима (BL) содержится в списке "опасных" режимов?
push ds
push di
push cx
push cs
pop ds
mov di,offset buf_1
mov cx,modes
main_8: cmp bl,[di]
je main_9
inc di
loop main_8
;Режим не найден в списке "опасных" (т.е. можно включать).
;Переходим на старый обработчик:
pop cx
pop di
pop ds
jmp short goto_old
;Это "опасный" режим - не включаем, выходим с AX=014Fh (признак ошибки):
main_9: pop cx
pop di
pop ds
mov ax,014fh
iret
;Определённые ниже данные должны быть описаны именно в указанном
;порядке - это используется при определении размера резидентной части.
modes dw ? ;Количество "опасных" режимов.
buf_1 db 256 dup (?) ;Список "опасных" режимов.
buf_2 db 256 dup (?) ;Вспомогательный буфер.
;Инициализация.
init: mov ax,4f00h ;Получаем информацию о поддерживаемых
mov di,offset buf_2 ;режимах (а заодно и о наличии VESA).
int 10h
cmp al,4fh
je vesa_ok
;VESA нет (в AL не 4Fh) - выходим с печатью сообщения об ошибке:
mov dx,offset err_text_1
jmp err_exit
;Из полученного списка режимов (сегмент и смещение которого находятся
;соответственно по адресам buf_2+16 и buf_2+14) копируем в buf_1 номера
;режимов, относящихся к VESA (у них 8-й бит равен 1):
vesa_ok: mov ax,word ptr buf_2+16
push ax
pop es
mov bx,word ptr buf_2+14
;ES:BX указывает на список режимов. Копируем, пока не встретим FFFFh -
;признак конца списка:
mov di,offset buf_1
xor cx,cx ;Количество VESA-режимов.
init_3: mov ax,es:[bx]
cmp ax,0ffffh
je init_1
and ah,1 ;Режим относится к VESA?
jz init_2
mov [di],al
inc di
inc cx
cmp cx,100h ;На всякий случай, чтобы buf_1 не переполнился.
je init_1
init_2: add bx,2
jmp short init_3
;Если VESA-режимов не обнаружено (CX=0) - выходим с печатью
;сообщения об ошибке:
init_1: and cx,cx
mov dx,offset err_text_2
jz err_exit
push cs
pop es
;Оставляем в buf_1 только номера "опасных" режимов - у которых
;разрешение больше указанного:
mov di,offset buf_1
push di
pop si
xor bx,bx ;Количество "опасных" режимов.
init_5: push di
push si
push cx
push bx
mov ax,4f01h ;Запрашиваем
mov ch,al ;ch:=1 ;информацию
mov cl,[di] ;о режиме.
mov di,offset buf_2
int 10h
pop bx
pop cx
pop si
pop di
cmp word ptr [buf_2+18],Max_X_Res
ja init_4
cmp word ptr [buf_2+20],Max_Y_Res
jbe init_6
init_4: mov al,[di]
mov [si],al
inc si
inc bx
init_6: inc di
loop init_5
;Если нет "опасных" режимов (BX=0) - выходим с печатью сообщения
;об ошибке:
and bx,bx
mov dx,offset err_text_3
jz err_exit
mov modes,bx ;Сохраняем количество "опасных" режимов.
mov ax,3510h ;Получаем и запоминаем
int 21h ;адрес старого
mov word ptr old_10h,bx ;обработчика INT 10h.
mov word ptr old_10h+2,es
mov dx,offset new_10h ;Устанавливаем
mov ax,2510h ;адрес нового
int 21h ;обработчика INT 10h.
;При выходе оставляем в резидентной части ровно столько байтов
;от buf_1, сколько используется:
mov dx,offset buf_1 ;Завершить и остаться
add dx,modes ;резидентом.
int 27h
;Печать сообщения об ошибке (его адрес задан в DX) и выход:
err_exit: mov ah,9
int 21h
ret
err_text_1 db 'VESA BIOS not found!',13,10,'$'
err_text_2 db 'VESA modes not found!',13,10,'$'
err_text_3 db 'Hi-res modes not found!',13,10,'$'
codesg ends
end begin
Размер резидентной части программы зависит от количества обнаруженных «опасных» режимов: каждый такой режим увеличивает длину на один байт. У меня программа занимает в памяти 352 байта.
Если вы используете драйвер UniVBE или аналогичный, то запускать программу следует после него.
|
Другие мои статьи о резидентных программах для DOS:
|