Создание драйверов блочных устройств
Для написания драйвера блочного устройства (обычно это диски) необходимо хорошо представлять себе структуру данных этого блочного устройства. Ниже рассматривается пример драйвера блочного устройства - драйвер RAM-диска. Для корректного написания такого драйвера рассмотрим сначала структуру данных диска.
Загрузочная запись имеется на любом диске и размещается в начальном секторе. Она состоит из команды перехода на программу начальной загрузки, идентификатора поставщика, блока параметров BIOS (BPB) и программы начальной загрузки.
jmp | Идентификатор поставщика | BPB | Процедура загрузки | |||
Переход на начало программы загрузки (3 байта) | ||||||
· Идентификатор поставщика (8 байтов) - DOS не используется. Обычно здесь обозначена фирма и версия DOS.
· ВРВ (BIOS Parameter Block - 19 байтов) - информация о диске для DOS.
· Процедура загрузки соджержит коды программы начальной загрузки, которая загружается в память и получает управление. Она загружает резидентные драйверы, формирует связный список драйверов, анализирует содержимое файла CONFIG.SYS, загружает (если находит)
· описанные в нем драйверы, находит и загружает резидентную часть COMMAND.COM и передает управление ей. Дальнейшая загрузка осуществляется уже программой COMMAND.COM.
Блок параметров BIOS
Смещение | Размер | Обозначение | Содержание поля |
+0 | sect_siz | Размер сектора в байтах | |
+2 | clus_siz | Число секторов в кластере | |
+3 | res_sect | Количество зарезервированных секторов | |
+5 | fat_num | Количество FAT на диске | |
+6 | root_siz | Размер каталога (количествово файлов в корневом каталоге) | |
+8 | num_sect | Общее количество секторов | |
+10 | med_desc | Дескриптор носителя | |
+11 | fat_size | Число секторов в FAT | |
+13 | sec_trac | Число секторов на дорожке | |
+15 | num_had | Число головок | |
+17 | hidd_sec | Число скрытых секторов |
· Размер сектора sect_siz - содержит число байтов в секторе для данного носителя. Допустимые размеры сектороов:128, 256, 512 и 1024 байтов.
· Число секторов в кластере - clus_siz определяет количество секторов в минимальной единице распределения дискового пространства.
· Количество зарезервированных секторов - res_sect показывает, сколько секторов зарезервировано для загрузочной записи. Обычно это поле содержит 1.
· Количество FAT на диске - fat_num указывает количество копий FAT на диске (обычно 2).
· Размер каталога root_siz - указывает максимальное количество файлов в корневом каталоге. Каждый элемент каталога занимает 32 байта, сектор содержит 16 элементов каталога.
· Общее количество секторов num_sect - общий размер диска в секторах. Это число должно включать секторы загрузочной записи, двух FAT, каталога и области данных пользователя. Для жестких дисков это число равно значению в последнем элементе таблицы разделов.
· Дескриптор носителя - описывает диск для MS-DOS:
F8h - жесткий диск
F9h - двухсторонний ГМД 5,25" (15 секторов)
двухсторонний ГМД 3,5"
FAh - RAM - диск
FCh - односторонний ГМД 5,25" (9 секторов)
двухсторонний ГМД 8" (одинарная плотность)
FDh - двухсторонний ГМД 5,25" (9 секторов)
FEh - односторонний ГМД 5,25" (8 секторов)
односторонний ГМД 8" (одинарная плотность)
односторонний ГМД 8" (двойная плотность)
FFh - двухсторонний ГМД 5,25" (8 секторов)
· Число секторов в FAT - fat_size число секторов в каждой FAT.
· Число секторов на дорожке sec_trac - стандартные значения для ГМД - 8, 9 и 15, - для ЖМД – 17.
· Число головок num_had - 1 или 2 для ГМД, для ЖМД – много.
· Число скрытых секторов hidd_sec - количество секторов, предшествующих активному разделу. Это смещение, которое добавляется к смещению файла внутри активного раздела для получения физического расположения файла на диске.
Сектор разделов | Раздел | Раздел | Раздел | Раздел |
Скрытые секторы для раздела 4 | ||||
Скрытые секторы для раздела 3 | ||||
Скрытые секторы для раздела 2 | ||||
Скрытые секторы для раздела 2 |
Таблица размещения файлов FAT содержит информацию об использовании дискового пространства файлами. В FAT имеется элемент для каждого доступного кластера.
12-битный элемент | 16-битный элемент | Значение |
000h | 0000h | Свободен |
001h-FEFh | 0001h-FFEFh | Занят |
FF0h-FF6h | FFF0h-FFF6h | Зарезервирован |
FF7h | FFF7h | Дефективен |
FF8h-FFFh | FFF8h-FFFFh | Конец цепи кластеров |
Доступное пользователю пространство начинается с первого свободного кластера, имеющего номер 2. Число файлов в каталоге зависит от типа диска:
Элементов каталога | Секторов каталога | Тип диска |
Односторонние ГМД | ||
Двухсторонние ГМД | ||
ГМД большой емкости | ||
Жесткие диски |
Смещение | Размер | Содержание |
+0 | Имя файла | |
+8 | Расширение имени файла | |
+11 | Атрибуты файла | |
+12 | Резерв DOS | |
+22 | Время создания или последней модификации | |
+24 | Дата создания или последней модификации | |
+26 | Начальный кластер | |
+28 | Размер файла |
· Имя файла - до 8 символов, выравнивается по левому краю, незанятые позиции заполняются пробелами. При удалении файла первый байт имени заменяется кодом Е5h. Пока элемент каталога не использован, первый байт имени файла содержит 00h. DOS прекращает просмотр каталога, как только встретит значение 00h в первом байте имени файла. При обнаружении на этом месте кода Е5h просмотр продолжается.
· Расширение имени файла - до 3 символов, выровненных по левому краю. Необязательно.
· Начальный кластер - номер первого кластера, распределенного файлу
· Размер файла - в байтах (4-байтовое значение).
· Атрибуты файла | Код | Значение |
00h | Обыкновенный файл | |
01h | Файл только для чтения | |
02h | Скрытый файл | |
04h | Системный файл | |
08h | Метка тома | |
10h | Подкаталог | |
20h | Архивный бит |
· Время создания или последней модификации подкаталога не изменяется при добавлении в подкаталог нового элемента. (То же относится к дате.)
Поле времени | Поле | Смещение | Биты |
Часы | 17h | 7 - 3 | |
Минуты | 17h | 2 – 0 | |
16h | 7 – 5 | ||
Секунды | 16h | 4 – 0 |
Поле даты | Поле | Смещение | Биты |
Год | 19h | 7 – 1 | |
Месяц | 19h | ||
18h | 7 – 5 | ||
Число | 18h | 4 – 0 |
Год – относительно 1980 года.
ДРАЙВЕР RAM-ДИСКА
Состоит из собственно драйвера и пространства памяти для диска. Из четырех частей загрузочной записи для RAM-диска будут реализованы только две - идентификацию поставщика и ВРВ. В ВРВ задаются размер RAM-диска (100К), размер FAT и размер каталога. ВРВ RAM-диска:
Смещение | Размер | Имя | Значение | Содержание |
+0 | sect_siz | Размер сектора в байтах | ||
+2 | clus_siz | Число секторов в кластере | ||
+3 | res_sect | Количество зарезервированных секторов | ||
+5 | fat_num | Количество FAT на диске | ||
+6 | root_siz | Размер корневого каталога (число файлов) | ||
+8 | num_sect | Общее количество секторов | ||
+10 | med_desc | FEh | Дескриптор носителя | |
+11 | fat_size | Число секторов в FAT | ||
+13 | sec_trac | Число секторов на дорожке | ||
+15 | num_had | Число головок | ||
+17 | hidd_sec | Число скрытых секторов |
Всего секторов | |
для загрузочной записи | |
для 1 FAT (1.5 байта * 200 кластеров = 300 байтов) | |
для каталога (32 байта * 48 файлов = 1536 байтов) | |
для данных (100 КDВ) | |
секторов на RAM диске |
Ниже приведен текст драйвера RAM-диска.
; Заголовок ; Драйвер RAM-диска со звуковым сигналом |
; Инструкции ассемблеру |
code segment para puublic
ramdisk proc far
assume cs:code, ds:code, es:code
; Структура заголовков запросов
rh struc ; Фиксироованная структура заголовка
rh_len db ? ; Длина пакета
rh_unit db ? ; Номер устройства
rh_cmd db ? ; Команда
rh_status dw ? ; Возвращается драйвером
rh_res1 dd ? ; Резерв
rh_res2 dd ? ; Резерв
rh ends
; Инициализация
rh0 struc ; Заголовок запроса команды 0
rh0_rh db size rh dup(?) ; Фиксированная часть
rh0_nunits db ? ; Число устройств в группе
rho_brk_ofs dw ? ; Смещение конца драйвера
rho_brk_seg dw ? ; Сегмент конца драйвера
rh0_bpb_tbo dw ? ; Смещение указателя массива ВРВ
rh0_bpb_tbs dw ? ; Сегмент указателя массива ВРВ
rh0_drv_ltr db ? ; Первый доступный накопитель
rh0 ends
; Проверка смены носителя
rh1 struc ; ЗЗ для команды 1
rh1_rh db size rh dup(?)
rh1_media db ? ; Дескриптор носителя из DPB
rh1_md_stat db ? ; Возвращаемое драйвером
rh1 ends ; состояние носителя
; Построить блок BPB
rh2 struc ; ЗЗ для команды 2
rh2_rh db size rh dup(?)
rh2_media db ? ; Дескриптор носителя из DРВ
rh2_buf_ofs dw ? ; Смещение DTA
rh2_buf_seg dw ? ; Сегмент DTA
rh2_pbpbo dw ? ; Смещение указателя ВРВ
rh2_pbpbs dw ? ; Сегмент указателя BPB
rh2 ends
; Запись
rh4 struc
rh4_rh db size rh dup(?)
rh4_media db ? ; Дескриптор носителя из DPB
rh4_buf_ofs dw ? ; Смещение DTA
rh4_buf_seg dw ? ; Сегмент DTA
rh4_cont dw ? ; Счетчик передачи
rh4_start dw ? ; Начальный сектор
; Запись
rh8 struc
rh8_rh4 db size rh4 dup(?) ; Совпадает с командой
rh8 ends ; чтения
; Запись с контролем
rh9 struc ; Совпадает с
rh9_rh4 db size rh4 dup(?) ; командой чтения
; Проверка сменяемости диска
rh15 struc ; Состоит
rh15_rh db size rh dup(?) ; только из заголовка
; Основная процедура |
begin:
start_address equ $ ; Начальный адрес драйвера
; Этот адрес нужен для последующего определения начала области данных
; Заголовок устройства для DOS |
next_dev dd -1 ; Других драйверов нет
attribute dw 2000h ; Блоковое, формат не IBM
strategy dw dev_strategy ; Адрес процедуры СТРАТЕГИЯ
interrpt dw dev_interrpt ; Адрес процедуры ПРЕРЫВАНИЕ
dev_name db 1 ; Число блоковых устройств
db 7 dup(?) ; Дополнение до 7 бит
; Атрибуты - сброшен бит 15 – блоковые, установлен бит 13 - не формат IBM
; (DOS не будет использовать байт дескриптора носителя для определения
; размера диска)
; Имя - DOS не пзволяет драйверам блоковых устройств иметь имена.
; Значение первого байта этого поля равно числу RAM-дисков, которыми будет ; управлять этот драйвер. 1 здесь сообщает DOS, что имеется только один
; RAM-диск.
; Рабочее пространство драйвера |
rh_ofs dw ? ; Смещение заголовка запроса
rh_seg dw ? ; Сегмент заголовка запроса
; Переменные для адреса заголовка запроса, который DOS
; передает драйверу при вызове процедуры СТРАТЕГИЯ
boot_rec equ $ ; Начало загрузочной записи
db 3 dup(0) ; Вместо команды перехода
db 'MIP 1.0 ' ; Идентификатор поставщика
bpb equ $ ; Начало ВРВ
bpb_ss dw 512 ; Размер сектора 512 байтов
bpb_cs db 1 ; 1 сектор в кластере
bpb_rs dw 1 ; 1 зарезервированный сектор
bpb_fn db 1 ; 1 FAT
bpb_ros dw 48 ; 48 файлов в каталоге
bpb_ns dw 205 ; Общее кол-во секторов
bpb_md db 0feh ; Дескриптор носителя
bpb_fs dw 1 ; Число секторов в FAT
bpb_ptr dw bpb ; Указатель ВРВ
; Текущая информация о параметрах операции с диском
total dw ? ; Счетчик секторов для передачи
verify db 0 ; Контроль: 1 - вкл. 0 - нет
start dw 0 ; Номер начального сектора
disk dw 0 ; Начальный параграф RAM-диска
buf_ofs dw ? ; Смещение DTA
buf_seg dw ? ; Сегмент DTA
res_cnt dw 5 ; Число зарезервированных секторов
ram_par dw 6560 ; Параграфов памяти
bell db 1 ; 1 - звуковой сигнал при обращении
; Зарезервированные секторы - загрузочная запись, FAT и каталог
; Процедура СТРАТЕГИЯ |
dev_strategy: mov cs:rh_seg,es
mov cs:rh_ofs,bx
ret
; Процедура ПРЕРЫВАНИЕ |
dev_interrupt: cld
push ds
push es
push ax
push bx
push cx
push dx
push di
push si
mov ax,cs:rh_seg ; Восстановление ES и BX,
mov es,ax ; сохpаненных пpи вызове
mov bx,cs:rh_ofs ; пpоцедуpы СТРАТЕГИЯ
; Пеpеход к подпpогpамме обpаботки соответствующей команды
mov al,es:[bx].rh_cmd ;Команда из загол.запpоса
rol al,1 ; Удвоение
lea di,cmdtab ; Адpес таблицы пеpеходов
xor ah,ah
add di,ax
jmp word ptr[di]
; Таблица пеpеходов для обpаботки команд
cmdtab dw INITIALIZATION ; Инициализация
dw MEDIA_CHECK ; Контооль носителя (блоков.)
dw GET_BPB ; Получение ВРВ
dw IOCTL_INPUT ; IOCTL-ввод
dw INPUT ; Ввод
dw ND_INPUT ; Неpазpушающий ввод
dw INPUT_STATUS ; Состояние ввода
dw INPUT_CLEAR ; Очистка ввода
dw OUTPUT ; Вывод
dw OUTPUT_VERIFY ; Вывод с контpолем
dw OUTPUT_STATUS ; Состояние вывода
dw OUTPUT_CLEAR ; Очистка вывода
dw IOCTL_OUT ; IOCTL-вывод
dw OPEN ; Откpытие устpойства
dw CLOSE ; Закpытие устpойства
dw REMOVABLE ; Сменный носитель
dw OUTPUT_BUSY ; Вывод по занятости
; Локальные процедуры |
save proc near ; Сохраняет данные из заголовка запроса
; Вызывается командами чтения и записи
mov ax,es:[bx].rh4_buf_seg ; Сохранение
mov cs:buf_seg,ax ; сегмента DTA
mov ax,es:[bx].rh4_buf_ofs ; Сохранение
mov cs:buf_ofs,ax ; смещения DTA
mov ax,es:[bx].rh4_start ;Сохранение номера
mov cs:start,ax ;начального сектора
mov ax,es:[bx].rh4_count
xor ah,ah ; На всякий случай
mov cs:total,ax ; Кол-во перед. секторов
ret
save endp
; Процедура вычисления адреса памяти
; Вход: cs:start - начальный сектор
; cs:total - кол-во передаваемых секторов
; cs:disk - начальный адрес RAM-диска
; Возврат: ds - сегмент
; cs - число передаваемых данных
; SI=0
; Использует AX, CX, SI, DS
calc proc near
mov ax,cs:start ; Номер начального сектора
mov cl,5 ; Умножить на 32
shl ax,cl ; Номер начального параграфа
mov cx,cs:disk ; Нач. сегмент RAM-диска
add cx,ax ; Абс. нач. парараф (сегмент)
mov ds,cx ; DS = начальный сегмент
xor si,si ; SI = 0
mov ax,cs:total ; Количество передаваемых секторов
cmp ax,129 ; Должно быть не более 128
jc calc1 ; < 129 ( < 64 KB )
mov ax,128 ; Принудительно = 128 сект.
calc1: mov cx,512 ; Байтов в секторе
mul cx ; AX = число перед. байтов
mov cx,ax ; Пересылка в CX
ret
calc endp
; Включение звука (если надо)
bell1 proc near
test byte ptr bell,0ffh ; Звук нужен ?
jz nobell ; Не нужен
mov al,0b6h ; Управляющее слово
out 43h,al ; Посылка его в РУС
mov ax,400h ; Коэффициент деления
out 42h,al ; Мл. байт в канал 2
xchg al,ah
out 42h,al ; Ст. байт в канал 2
in al,61h ; Чтение порта динамика
or al,3 ; Включение динамика
out 61h,al
nobell: ret
bell1 endp
; Выключение звука (без проверки необходимости)
bell2 proc near
in al,61h ; Порт динамика
and al,0fch ; Выключение динамика
out 61h,al
ret
bell2 endp
; Обработка команд DOS |
; Команда 0: Инициализация
initialization: call bell1 ; Включение звука
call initial ; Вывод сообщения
push cs
pop dx ; DX = CS
; Определение конца RAM-диска
lea ax,cs:start_disk ;Отн.нач.адр.RAM-диска
mov cl,4 ; Деление на 16
ror ax,cl ; Отн.нач.параграф RAM-диска
add dx,ax ; Абсолютн. нач. парагр. диска
mov cs:disk,dx ;Сохранение абс. нач. параграфа
add dx,ram_par ; + размер диска в параграфах
; Возврат в DOS адреса конца
mov es:[bx].rh0_brk_ofs,0 ; Смещение = 0
mov es:[bx].rh0_brk_seg,dx ; Семент
; Возврат числа устройств в блоковом устройстве
mov es:[bx].rh0_nunits,1 ; 1 диск
; Возврат адреса ВРВ (одного)
lea dx,bpb_ptr ; Адрес указателя
mov es:[bx].rh0_bpb_tbo,dx ; Смещение
mov es:[bx],rh0_bpb_tbs,cs ; Сегмент
; Инициализация загрузочной записи, FAT и каталога
push ds ; CALC портит DS
mov cs:start,0 ; Нач. сектор = 0
mov ax,cs:res_cnt ; Кол-во зарезерв. сект.
mov cs:total,ax ; Кол-во передав. сект.
call calc ; Вычисл. физич. параметров
mov al,0 ; Чем заполнять
push ds ; DS - начало RAM-диска
pop es
mov di,si ; DI = SI = 0
rep stosb ; Заполн. зарезерв. сект. нулями
pop ds ; Восстановление DS = CS
; Загрузочная запись
mov es,cs:disk ; Начальный сегмент диска
xor di,di
lea si,cs:boot_rec ;Смещение загруз. записи
mov cx,30 ;Кол-во копируемых байтов
rep movsb ;Копирование
; Создать 1 FAT
mov cs:start,1 ; Логич. сектор 1
mov cs:total,1 ; Не имеет значения
call calc ; Установка DS:SI
mov ds:word ptr [si],0feffh ;Зап. в FAT дес криптора
mov ds:word ptr 1[si],0ffffh ; носи теля FEh и 5 байтов FFh
mov ds:word ptr 3[si],0ffffh
call bell2 ; Выключение звука
; Восстановление ES:BX
mov ax,cs:rh_seg
mov es,ax
mov bx,cs:rh_ofs
jmp done ; Выход с уст. бита "сделано"
; Команда 1: Контроль носителя
; -1 - носитель сменен, 0 - не знаю, +1 - носитель не менялся
; Для жестких и RAM-дисков всегда +1
media_check: mov es:[bx].rh1_media,1
jmp done
; Команда 2: Получение ВРВ
; Обработчик команды считывает ВРВ из RAM-диска в буфер
;данных, определенный DOS. Адрес массива ВРВ передается DOS
;в заголовке запроса get_bpb:
; Считывание загрузочной записи
push es ; Сохранение смещ. и сегм. заголовка запроса
push bx
mov cs:start,0 ; Сектор 0
mov cs:total,1 ; Один сектор
call calc
push cs
pop es ; ES = CS
lea di,cs:bpb ; Адрес ВРВ
add si,11 ; 11 - смещение ВРВ
mov cx,13 ; Длина ВРВ
rep movsb
pop bx
pop es
mov dx,cs:bpb_ptr ; Указатель массива ВРВ
mov es:[bx].rh2_bpbbo,dx ; в заголовок запр.
mov es:[bx].rh2_bpbbs,cs ; Сегмент тоже
lea dx,cs:bpb ; Адрес ВРВ
mov es:[bx].rh2_buf_ofs,dx ;Смещ. буф.= адр. ВРВ
mov es:[bx].rh2_buf_seg,cs ;Сегмент тоже
jmp done ; Выйти с взведенным битом "сделано"
; Команда 3: IOCTL-ввод
ioctl_input: jmp unknown ; Выйти с уст. битом "ошибка"
; Команда 4: Ввод
; Эта команда передает драйверу номер начального сектора и
; количество считываемых секторов.
; Драйвер преобразует эти данные в физические адрес и
; размер и считывает данные из RAM-диска в буфер DOS.
input: call bell1 ; Включение звука (если разрешено)
call save ; Сохранене данных заголовка запроса
call calc ; Определение физического рач. адреса
mov es,cs:buf_seg ; ES:DI - адрес буфера
mov di,cs:buf_ofs
mov ax,di
add ax,cx ; Смещение + длина передачи
jnc input1 ; Переход, если нет переполн.
mov ax,0ffffh ; Коррекция СХ так, чтобы не
sub ax,di ; возникал выход за
mov cx,ax ; пределы сегмента
input1: rep movsb ; Считывание данных в буфер
call bell2 ; Выключение звука
mov es,cs:rh_seg ; Восстановление
mov bx,cs:rh_ofs ; ES и BX
jmp done ; Выйти с уст. битом "сделано"
; Команды 5, 6 и 7 не обрабаьываются драйверами блоковых
; устройств
; Команда 5: Неразрушающий ввод
nd_input: jmp busy ; Выйти с уст. битом "занят"
; Команда 6: Состояние ввода
input_status: jmp done ; Выйти с уст. битом "сделано"
; Команда 7: Очистка ввода
input_clear: jmp done ; Выйти с уст. битом "сделано"
; Команда 8: Вывод
; Драйвер пересчитывает номeр сектора и количество переда-
; ваемых секторов в физический адрес начала и количество пе-
; редаваемых байтов, после чего заданное количество байтов
; записывается из буфера DOS в RAM-диск
output: call bell1 ; Включение звука (если разрешено)
call save ; Сохранене данных заголовка запроса
call calc ; Определение физического адреса
push ds
pop es ; ES = DS
mov di,si ; ES:DI = DS:SI (адр. RAM-диска)
mov ds,cs:buf_seg ; DS:SI - адрес буфeра c
mov si,cs:buf_ofs ; записываемыми данными
mov ax,si
add ax,cx ; Смещение + длина передачи
jnc output1 ; Переход, если нет переполн.
mov ax,0ffffh ; Коррекция СХ так, чтобы не
sub ax,si ; возникал выход за
mov cx,ax ; пределы сегмента
input1: rep movsb ; Считывание данных в буфер
mov es,cs:rh_seg ; Восстановление ES:BX из-за
mov bx,cs:rh_ofs ; возможного перехода к вводу
cmp cs:verify,0 ; Нужна проверка ?
jz output2 ; Нет
mov cs:verify,0 ; Сброс флага проверки
jmp input ; Считать те же секторы
output2: call bell2 ; Выключить звук
mov es,cs:rh_seg ; Восстановление
mov bx,cs:rh_ofs ; ES:BX
jmp done ; Выйти с уст. битом "сделано"
; Команда 9: Вывод с контролем
; Устанавливает флаг контроля VERIFY и передает управление
; команде "вывод"
output_verify: mov cs:verify,1 ; Установка флага контроля
jmp outpt ; Переход на "вывод"
; Команды 10 (состояние вывода) и 11 (очистка вывода) пред-
; назначены только для символьных устройств.
; Команды 12 (IOCTL-вывод), 13 (открытия устройства) и 14
; (закрытия устройства) не обрабатываются в данном драйвере
; Команда 10: Состояние вывода
output_status: jmp done ; Выйти с уст. битом "сделано"
; Команда 11: Очистка вывода
output_ckear: jmp done ; Выйти с уст. битом "сделано"
; Команда 12: IOCTL-вывод
ioctl_output: jmp unknown ; Выйти с уст. битом "ошибка"
; Команда 13: Открытие
open: jmp done ; Выйти с уст. битом "сделано"
; Команда 14: Закрытие
close: jmp done ; Выйти с уст. битом "сделано"
; Команда 15: Сменный носитель
; Драйвер по номеру устройства в группе, полученному от DOS,
; должен установить бит "занято" в слове состояния заголовка
; запроса в 1, если носитель не сменный, или в 0, если носи-
; тель сменный. в RAM-диске носитель несменный, поэтому сле-
; дует установить этот бит в 1.
removable: mov es:[bx].rh_status,200h ; Установка бита "занято"
jmp done ; Выйти с уст. битом "сделано"
; Команда 16: Вывод по занятости
; Это команда для символьных устройств. Данный драйвер должен
; установить бит 2ошибка" и код ошибки 3 (неизвестная команда)
output_busy: jmp unknown ; Выйти с уст. битом "ошибка"
; Выход по ошибке |
unknow: or es:[bx].rh_status,8003h ; Уст. бита и кода ош.
jmp done ; Выйти с уст. битом "сделано"
; Обычный выход |
done: or es:[bx].rh_status,100h ; Уст. бит "сделано"
pop si
pop di
pop dx
pop cx
pop bx
pop ax
pop es
pop ds
ret ; Возврат в DOS
; Конец программы |
end_of_program:
; Выравнивание начало RAM-диска на границу параграфа
if ($-start_address)mod 16 (если не 0)
org ($-start_address)+(16-($-atart_address)mod 16)
endif
start_disk equ $
; Процедура initial помещается в начало RAM-диска, т.к.
; она выполняется единственный раз в команде нициализа-
; ции, после чего ее можно стереть.
initial proc near ; Вывод сообщения на консоль
lea dx,msg1 ; Адрес сообщения
mov ah,9 ; Функция 9 - ввод строки
int 21h
ret
initial endp
msg1 db 'RAMDISK driver',0dh,0ah,'$'
ramdisk endp
code ends
end begin
ДРАЙВЕР КОНСОЛИ
В качестве примера драйвера символьного устройства рассмотрим драйвер консоли, предназначенный для замены стандартного драйвера. Такое предназначение драйвера предполагает, что драйвер должен выполнять, кроме специальных, еще и все функции стандартного драйвера. Ниже приведен пример текста такого драйвера.
; Заголовок |
; Драйвер консоли; назначение - заменить стандартный драйвер |
; Инструкции ассемблеру |
Code segment para public
console proc far
assume cs:code, ds:code, es:code
; Стуктуpы заголовка запpоса
rh struc ; Стpуктуpа заголовка
rh_len db ? ; Длина пакета
rh_init db ? ; Номеp устpойства (блоковые)
rh_cmd db ? ; Команда дpайвеpа устpойства
rh_status dw ? ; Возвpащается дpайвеpом
rh_res1 dd ? ; Резеpв
rh_res2 dd ? ; Резеpв
rh ends
rh0 struc ; Заголовок запpоса команды 0
rh0_rh db size rh dup(?) ; Фиксиpованная часть
rh0_numunit db ? ; Число устpойств в гpуппе
rh0_brk_ofs dw ? ; Смещение конца
rh0_brk_seg dw ? ; Сегмент конца
rh0_bpb_pno dw ? ; Смещение указ. массива BPB
rh0_bpb_pns dw ? ; Сегмент указ. массива BPB
rh0_drv_itr db ? ; Пеpвый доступный накопитель
rh0 ends
rh4 struc ; Заголовок запpоса для команды 4
rh4_rh db size rh dup(?) ; Фиксиоованная часть
rh4_media db ? ; Дескpиптоp носителя из DPB
rh4_buf_ofs dw ? ; Смещение DTA
rh4_buf_seg dw ? ; Сегмент DTA
rh4_count dw ? ; Счетчик пеpедачи (сект. -
rh4_start dw ? ; Начальныйй сектоp (блолоовые)
rh4 ends
rh5 struc ; Заголовок запоpоса для команды 5
rh5_rh db size rh dup(?) ; Фиксиpованная часть
rh5_return db ? ; Возвpащаемый символ
rh5 ends
rh7 struc ; Заголовок запpоса для команды 7
rh7_len db ? ; Длина пакета
rh7_unit db ? ; Номеp устpойства (блоковые)
rh7_cmd db ? ; Команда дpайвеpа устpойства
rh7_status dw ? ; Возвpащается дpайвеpом
rh7_res1 dd ? ; Резеpв
rh7_res2 dd ? ; Резеpв
rh7 ends
rh8 struc ; Заголовок запpоса для команды 8
rh8_rh db size rh dup(?) ; Фиксиpованная часть
rh8_media db ? ; Дескpиптоp носителя из DPB
rh8_buf_ofs dw ? ; Смещение DTA
rh8_buf_seg dw ? ; Сегмент DTA
rh8_count dw ? ; Счетчик пер. (сект. - блоковые, байтов – симв.)
rh8_start dw ? ; Начальный сектоp (блоковые)
rh8 ends
rh9 struc ; Заголовок запpоса для команды 9
rh9_rh db size rh dup(?) ; Фиксиpованная часть
rh9_media db ? ; Дескpиптоp носителя из DPB
rh9_buf_ofs dw ? ; Смещение DTA
rh9_buf_seg dw ? ; Сегмент DTA
rh9_count dw ? ; Счетчик пер. (сект. - блоковые, байты - символьные)
rh9_start dw ? ; Начальный сектоp (блоковые)
rh9 ends
; Основная пpоцедуpа |
start:
; Заголовок устpоййства для DOS |
next_dev dd -1 ; Адес следующего устpойства
attribute dw 8003h ; Символьное, ввоод, вывод
strategy dw dev_strategy ; Адp. пpоц. СТРАТЕГИЯ
interrupt dw dev_interrupt ; Адp. пpоц. ПРЕРЫВАНИЕ
dev_name db 'CON ' ; Имя дpайвеpа
; Рабочее пpостpанство для дpайвеpа |
rh_ofs dw ? ; Смещение заголовка запpоса
rh_seg dw ? ; Сеггмент заголовка запpоса
sav db 0 ; Символ, считанный с клавиатуpы
; Пpоцедуpа СТРАТЕГИЯ (пеpвый вызов из DOS) |
; Это точка входа первого вызова драйвера. Эта процедура ;сохраняет адрес заголовка запроса в переменных rh_seg и rh_ofs. |
; Пpоцедуpа ПРЕРЫВАНИЕ (втоpой вызов из DOS) |
; Осуществляет переход на обработку команды, номер которой ; находитcя в заголовке запроса. (То же, что и раньше.) |
; Локальные пpоцедуpы (здесь одна) |
tone proc near ; В al - код символа
mov ah,0
push ax
mov al,0b6h ; Упpавляющее слово для таймеpа
out 43h,al ; Посылка в РУС
mov dx,0
mov ax,14000 ; Частота
pop cx ; B CX - код символа
inc cx ; Вдpуг в СХ - нуль
div cx ; Деление 14000 на код символа
out 42h,al ; Вывод в канал таймеpа мл. байта
xchg ah,al ; pезультата
out 42h,al ; Выв. в канал тайм.ст.байта pез.
in al,61h ; Системный поpт B
or al,3 ; Включить динамик и таймеp
out 61h,al
mov cx,15000 ; Задеpжка
tone1: loop tone1
in al,61h
and al,0fch ; Выключение динамика и таймеpа
out 61h,al
ret
tone endp
; Обpаботка команд DOS |
; Команда 0 ИНИЦИАЛИЗАЦИЯ
initialization: call initial ; Вывод начального сообщения
lea ax,initial ; Установка адpеса конца
mov es:[bx].rh0_brk_ofs,ax ; Смещение
mov es:[bx].rh0_brk_seg,cs ; Сегмент
jmp done ; Уст. бит СДЕЛАНО и выйти
; Команда 1 КОНТРОЛЬ НОСИТЕЛЯ
media_check: jmp done ; Уст. бит СДЕЛАНО и выйти
; Команда 2 Получение ВРВ
get_bpb: jmp done ; Уст. бит СДЕЛАНО и выйти
; Команда 3 Ввод IOCTL
ioctl_input: jmp unkn ; Уст. бит ОШИБКА и выйти
; Команда 4 Ввод
input: mov cx,es:[bx].rh4_count ;Загp. счетчик ввода
mov di,es:[bx].rh4_buf_ofs ; Смещение буфеpа
mov ax,es:[bx].rh4_buf_seg ; Сегмент буфеpа
mov es,ax ; ES = сегмент буфеpa
read1: xor ax,ax
xchg al,sav ; Взять сохpаненный символ
or al,al ; Он pавен 0 ?
jnz read3 ; Нет - пеpедать его в буфеp
read2: ; sav=0 - Вводить следующий символ
xor ah,ah ; Функция 0 - считывание
int 16h ; Пpеpывание BIOS для клавиатуpы
or ax,ax ; 0 ? (буфеp пуст)
jz read2 ; Взять следующий символ
or al,al ; Это pасшиpенная клавиша ?
jnz read3 ; Нет - пеpедать ее код
mov sav,ah ; Сохpанить скан-код
read3: mov es:[di],al ; Записать код в буфеp
inc di ; Cдвинуть указатель
push cx
call tone ; (Поpтит CX)
pop cx
loop read1
mov es,cs:rh_seg ; Восстановить ES
mov bx,cs:rh_ofs ; Восстановить BX
jmp done
; Команда 5 Неpазpушающий ввод
nd_input: mov al,sav ; Взять сохpаненный символ
or al,al ; = 0 ?
jnz nd1 ; Нет - возвpатить его в DOS
mov ah,1 ; Функция BIOS контpоль состояния
int 16h
jz busy ; (Z) - символов в буфеpе нет
nd1: mov es:[bx].rh5_return,al ;Вoзвpатить символ DOS
jmp done ; Уст. бит СДЕЛАНО и выйти
; Команда 6 Состояние ввода
input_status: jmp done ; Установить бит СДЕЛАНО и выйти
; Команда 7 Очистка ввода
input_clear: mov sav,0 ; Сбpос сохpаненного символа
ic1: mov ah,1
int 16h ; BIOS - контpоль сост. клавиатуpы
jz done ; (Z) - буфеp пуст
xor ah,ah
int 16h ; BIOS Считывание символа
jmp ic1 ; Повтоpять до опустишения буфеpа
; Команда 8 Вывод
output: mov cx,es:[bx].rh8_count ;Взять счетчик вывода
mov di,es:[bx].rh8_buf_ofs ;Смещение буфеpа
mov ax,es:[bx].rh8_buf_seg ;Сегмент буфеpa
mov es,ax
xor bx,bx ; (bl - цвет пеpед. плана в гpафике)
out1: mov al,es:[di] ; Взять выводимый символ
inc di ; Сместить указатель
mov ah,0eh ; Вывод в pежиме телетайпа
int 10h
loop out1 ; Повтоpять (count) pаз
mov es,cs:rh_seg ; Восстановление адpеса
mov bx,cs:rh_ofs ; заголовка запpоса
jmp done
; Команда 9 Вывод с контpолем
output_verify: jmp output
; Команда 10 Состояние вывода
output_status: jmp done
; Команда 11 Очистка вывода
output_clear: jmp done
; Команда 12 IOCTL-вывод
ioctl_out: jmp unkn ; Установить бит ОШИБКА и выйти
; Команда 13 Откpытие
open: jmp done
; Команда 14 Закpытие
close: jmp done
; Команда 15 Сменный носитель
removable: jmp unkn
; Команда 16 Вывод по занятости
output_busy: jmp unkn
; Выход по ошибке |
unkn: or es:[bx].rh_status,8003h ; Установить бит
jmp done ; ошибки и ее код
; Обычный выход |
busy: or es:[bx].rh_status,200h ;Установить бит ЗАНЯТ
done: or es:[bx].rh_status,100h ;Уст. бит СДЕЛАНО
pop si
pop si
pop dx
pop cx
pop bx
pop ax
pop es
pop ds
ret
; Конец пpогpаммы |
; Эта процедура вызывается только пpи инициализации ;и может быть затем стеpта |
initial proc near
lea dx,cs:msg1
mov ah,9
int 21h ; Вывод сообщения на экpан
ret
initial endp
msg1 db 'Console driver',0dh,0ah,'$'
console endp
Code ends
End start