Листинг 6-10. Исходный текст драйвера RAM-диска
----------------------------------------------------------------------
PAGE 60,132
; ************ RDISK.ASM : MS-DOS ДРАЙВЕР RAM-ДИСКА ******************
;
; Этот файл содержит исходный текст простого MS-DOS драйвера RAM-диска
; эмулирующего 360K флоппи-диск.
;
; В этом примере демонстрируются основные принципы построения драйвера
; устройств, включая один из методов, который можно использовать для
; отладки драйверов. Для установки этого драйвера включите в файл
; CONFIG.SYS строку "DEVICE=RDISK.SYS"
;
; ============ ВСПОМОГАТЕЛЬНЫЕ ФАЙЛЫ ДЛЯ ДРАЙВЕРА ====================
;
INCLUDE driver.inc ; Константы для MS-DOS драйвера
IFDEF DEBUG
INCLUDE biosio.inc ; Определения для отладки
ENDIF
;
; ============ КОНСТАНТЫ =============================================
;
; Ограничения,накладываемые версией MS-DOS на максимальный код команды
;
CMD_PRE_30 EQU 00Ch ; до MS-DOS версии 3.00
CMD_PRE_32 EQU 00Fh ; до MS-DOS версии 3.20
CMD_32 EQU 018h ; начиная с версии 3.20
;
IFDEF DEBUG
CR EQU 0Ah ; используются в отладочных
LF EQU 0Dh ; сообщениях
ENDIF
;
PAGE
;
; ============ ШАБЛОНЫ СТРУКТУР ======================================
;
request EQU es:[di] ; указатель на блок запроса
;
; Структура заголовка запроса
;
reqhdr STRUC
rlength db ? ; размер блока запроса
unit db ? ; номер устройства
command db ? ; код команды
status dw ? ; возвращаемый статус
db 8 DUP (?) ; зарезервировано
reghdr ENDS
;
; Структура блока запроса для команды INIT
;
inithdr STRUC
db (type reqhdr) DUP (?)
units db ? ; количество устройств
endadro dw ? ; смещение и сегмент
endadrs dw ? ; адреса завершения
bpbtabo dw ? ; смещение и сегмент
bpbtabs dw ? ; таблицы BPB
devnum db ? ; номер устройства
inithdr ENDS
;
; Структура блока запроса для команды MEDIA CHECK
;
mchkhdr STRUC
db (type reqhdr) DUP (?)
mbd db ? ; описатель носителя
chande dw ? ; статус замены
volume dd ? ; указатель на имя тома
mchkhdr ENDS
;
; Структура блока запроса для команды BUILD BPB
;
bpbhdr STRUC
db (type reqhdr) DUP (?)
db ? ; описатель носителя
dd ? ; указатель на FAT
bpbptro dw ? ; смещение BPB
bpbptrs dw ? ; сегмент BPB
bpbhdr ENDS
;
; Структура блока запроса для команд чтения/записи
;
iohdr STRUC
db (type reqhdr) DUP(?)
db ? ; описатель носителя
bufprt dd ? ; адрес буфера
count dw ? ; кол-во байт/секторов
start dw ? ; # начального сектора
nuvol dd ? ; адрес нов. имени тома
iohdr ENDS
;
; Структура блока параметров BIOS (BPB)
;
bpbstrc STRUC
bps dw ? ; количество байтов в секторе
spau db ? ; кол-во секторов в кластере
nrs dw ? ; кол-во зарезервир. секторов
nft db ? ; количество копий FAT
nde dw ? ; кол-во элементов директория
nls dw ? ; кол-во логических секторов
md db ? ; байт описателя носителя
nfs dw ? ; размер FAT в секторах
bpbstrc ENDS
;
PAGE
;
; ============= НАЧАЛО КОДА ДРАЙВЕРА =================================
;
_TEXT SEGMENT BYTE PUBLIC 'CODE'
ASSUME CS:_TEXT, DS:_TEXT, ES:NOTHING
ORG 0
ORIGIN EQU $
;
; ============= ЗАГОЛОВОК ДРАЙВЕРА ===================================
;
dw -1,-1 ; указатель на след. драйвер
dw AT_IOCTL OR AT_OCRM OR AT_NET
dw offset STRATEGRY ; смещение СТРАТЕГИЙ
dw offset ПРЕРЫВАНИЙ ; смещение ПРЕРЫВАНИЙ
db 1,'CDEVICE' ; кол-во устройств/имя
;
; ============= ТАБЛИЦА АДРЕСОВ ОБРАБОТЧИКОВ КОМАНД ==================
;
JUMPTAB LABEL WORD
dw offset INIT ; 0 - инициализация
dw offset MEDIA_CHECK ; 1 - проверка носителя
dw offset BUILD_BPB ; 2 - построить BPB
dw offset IOCTL_INPUT ; 3 - IOCTL ввод
dw offset READ ; 4 - ввод из устр-ва
dw offset READ_NOWAIT ; 5 - неразруш. ввод
dw offset INPUT_STATUS ; 6 - ввод статуса
dw offset INPUT_FLUSH ; 7 - сбросить ввод
dw offset WRITE ; 8 - вывод на устр-во
dw offset WRITE_VERIFY ; 9 - вывод с проверкой
dw offset OUTPUT_STATUS ; A - вывод статуса
dw offset OUTPUT_FLUSH ; B - сбросить вывод
dw offset IOCTL_OUTPUT ; C - вывод IOCTL
dw offset DEVICE_OPEN ; D - открыть устр-во
dw offset DEVICE_CLOSE ; E - закрыть устр-во
dw offset REMOVABLE ; F - носитель сменный?
dw offset NO_COMMAND ; 10
dw offset NO_COMMAND ; 11
dw offset NO_COMMAND ; 12
dw offset GENERIC_IOCTL ; 13 - Generic IOCTL
dw offset NO_COMMAND ; 14
dw offset NO_COMMAND ; 15
dw offset NO_COMMAND ; 16
dw offset GET_LOGICAL ; 17 - получить/устано-
dw offset SET_LOGICAL ; 18 - вить лог.устр-во
;
; ============ ОБЛАСТЬ ДАННЫХ ДРАЙВЕРА ===============================
;
reg_ptr dd ? ; адрес блока запроса
max_cmd db CMD_PRE_30 ; максимально допустимый код
; ; команды
save_ss dw ? ; значение SS на входе
save_sp dw ? ; значение SP на входе
;
PAGE
;
; ============ ПРОГРАММА СТРАТЕГИЙ ====================================
;
STRATEGY PROC FAR
mov cs:word ptr [reg_ptr],bx
mov cs:word ptr [reg_ptr+2],es
ret
strategy ENDP
;
; ============ ПРОГРАММА ПРЕРЫВАНИЙ ===================================
;
INTERRUPT PROC FAR
push ax ; сохранить все рабочие
push cx ; регистры
push dx
push bx
push bp
push si
push di
push ds
push es
;
push cs ; определим локальный сегмент
pop ds ; данных
;
mov word ptr save_ss,ss ; сохраним входное
mov word ptr save_sp,sp ; значение SS и SP
;
mov bx,cs ; установим локальный
mov ax,offset local_stack - 2 ; стек
mov ss,bx
mov sp,ax
;
les di,[req_ptr] ; получить адрес блока
mov bl,request.command ; запроса и команду
;
; установим заранее код ошибки на случай если команда неверная
;
mov ax,(ST_ERROR OR UNKNOWN_COMMAND)
cmp bl,[max_cmd] ; команда поддерживается ?
ja exit ; нет - отвергаем ее
;
; Выдаем указанную команду на выполнение соответствующему обработчику.
; Каждый обработчик получает управление с CS и DS установленными на
; сегмент драйвера и ES:DI указывающем на блок запроса. Свой статус
; обработчики возвращают в регистре AX.
;
xor bh,bh ; BX - индекс в таблице
shl bx,1 ; команд
IFDEF DEBUG
call print_command ; выдаем имя обрабатываемой
ENDIF ; команды
call word ptr jumptab[bx] ; вызываем обработчик
;
; Перешлем статус из регистра AX в слово состояния блока запроса
;
exit: push cs ; установка локального
pop ds ; сегмента данных
;
les di,[req_ptr] ; получим адрес блока запроса
or ax,ST_DONE ; установим бит DONE
mov request.status,ax ; сохраним статус
;
mov ss,word ptr save_ss ; восстановим значение
mov sp,word ptr save_sp ; регистров SS:SP
;
pop es ; восстановим содержимое
pop ds ; регистров
pop di
pop si
pop bp
pop bx
pop dx
pop cx
pop ax
ret
interrupt ENDP
;
PAGE
;
; ============ ОБРАБОТЧИКИ КОМАНД ====================================
;
NO_COMAND PROC NEAR ; неподдерживаемая команда
ret ; возврат с ошибкой
NO_COMMAND ENDP
;
MEDIA_CHECK PROC NEAR ; 1 - проверка носителя
mov request.change,NotChanged
xor ax,ax
ret
MEDIA_CHECK ENDP
;
BUILD_BPB PROC NEAR ; 2 - построить BPB
mov request.bpbptro,offset bpb
mov request.bpbptrs,cs
xor ax,ax
ret
BUILD_BPB ENDP
;
IOCTL_INPUT PROC NEAR ; 3 - ввод IOCTL
xor ax,ax
ret
IOCTL_INPUT ENDP
;
READ PROC NEAR ; 4 - ввод из устройства
call verify ; проверка и установка параметров
jc rd_err ; выход по ошибке
les di,request.bufptr ; считываем в буфер
rep movsw ; передача
xor ax,ax ; нет ошибок
rd_err:
ret
READ ENDP
;
READ_NOWAIT PROC NEAR ; 5 - неразрушающий ввод
xor ax,ax ; без ожидания
ret
READ_NOWAIT ENDP
;
INPUT_STATUS PROC NEAR ; 6 - ввод статуса
xor ax,ax
ret
INPUT_STATUS ENDP
;
INPUT_FLUSH PROC NEAR ; 7 - сбросить входную очередь
xor ax,ax
ret
INPUT_FLUSH ENDP
;
WRITE PROC NEAR ; 8 - вывод на устройство
call verify ; проверка и установка параметров
jc wr_err ; выход при ошибке
push ds ; сохраним сегмент "сектора"
lds si,request.bufptr ; записываем из буфера
pop es ; на диск
xor di,di ; с нулевым смещением
rep movsw ; передача
xor ax,ax ; нет ошибок
wr_err:
ret
WRITE ENDP
;
WRITE_VERIFY PROC NEAR ; 9 - вывод с проверкой
call write
ret
WRITE_VERIFY ENDP
;
OUTPUT_STATUS PROC NEAR ; A - вывод статуса
xor ax,ax
ret
OUTPUT_STATUS ENDP
;
OUTPUT_FLUSH PROC NEAR ; B - сбросить выходную очередь
xor ax,ax
ret
OUTPUT_FLUSH ENDP
;
IOCTL_OUTPUT PROC NEAR ; C - вывод IOCTL
xor ax,ax
ret
IOCTL_OUTPUT
;
DEVICE_OPEN PROC NEAR ; D - открыть устройство
xor ax,ax
ret
DEVICE_OPEN ENDP
;
DEVICE_CLOSE PROC NEAR ; E - закрыть устройство
xor ax,ax
ret
DEVICE_CLOSE ENDP
;
REMOVABLE PROC NEAR ; F - носитель сменный ?
mov ax,ST_BUSY ; нет !
ret
REMOVABLE ENDP
;
GENERIC_IOCTL PROC NEAR ; 13 - групповой IOCTL запрос
xor ax,ax
ret
GENERIC_IOCTL ENDP
;
GET_LOGICAL PROC NEAR ; 17 - получить имя логического
xor ax,ax ; диска
ret
GET_LOGICAL ENDP
;
SET_LOGICAL PROC NEAR ; 18 - установить имя логического
xor ax,ax ; диска
ret
SET_LOGICAL ENDP
;
PAGE
; ------------ Подпрограммы обработки запросов -----------------------
; Эти подпрограммы вызываются для обработки параметров любого запроса
; на ввод/вывод.
; На входе :
; ES:DI - содержит адрес блока запроса
; Действия :
; Проверка параметра "номер сектора" на допустимость.
; Преобразование этого параметра в "сегмент:смещение".
; Выровнять счетчик для предотвращения "перекрытия".
; На выходе :
; DS:SI - содержит адрес "сектора" в RAM-диске
; ES:DI - содержит адрес блока запроса
; CX - содержит количество передаваемых слов.
;
verify PROC NEAR
; проверим,что номера начального и конечного секторов лежат в пределах
; от 0 до N.
mov cx,request.start ; сравним номер начального
cmp cx,bpb.nls ; сектора с количеством
jae out_of_range ; логических секторов
add cx,request.count ; найдем номер конечного
dec cx ; сектора и тоже сравним
cmp cx,bpb.nls ; если номера секторов
jb in_range ; нормальные то продолжим
; заданные секторы не содержатся на диске
out_of_range:
mov ax,ST_ERROR OR SECTOR_NOT_FOUND
mov request.count,0 ; ничего не было передано
stc ; возвращаемся с ошибкой
ret
; вычислим сегментный адрес начального сектора
in_range:
mov ax,bpb.bps ; количество байт в секторе
mov cl,4 ; разделим на 16 для получения
shr ax,cl ; размера в параграфах
mul request.start ; смещение параграфа относи-
; тельно начала диска
add ax,RPARA ; смещение параграфа относи-
mov dx,cs ; тельно CS
add ax,dx ; абсолютное смещ. параграфа
mov si,ax ; сохраним сегмент в SI
; вычислим и проверим счетчик передаваемых данных
mov ax,bpb.bps ; размер сектора в байтах
mul request.count ; счетчик передачи в байтах
cmp dx,0 ; проверим на корректность
jne out_of_range
; выровняем счетчик в AX для предотвращения перекрытия
mov cx,word ptr request.bufptr
cmp ax,0 ; смещение = 0
je set_size
neg cx ; остаток = 64K - смещение
cmp cx,ax ; буфера
jae set_size ; если остаток меньше счетчика,
mov ax,cx ; то передаем только остаток
; установим количество передаваемых секторов и счетчик передачи
set_size:
mov cx,ax ; счетчик передачи в байтах
shr cx,1 ; преобразуем в счетчик слов
div bpb.bps ; (DX был 0) кол-во секторов
mov request.count,ax ; сохраним счетчик передачи
; загрузим в DS:SI адрес блока в памяти
mov ds,si
xor si,si
; установим направление передачи и вернемся без ошибок
cld
clc
ret
verify ENDP
;
IFDEF DEBUG
INCLUDE biosio.asm
PAGE
;
; ************ КОД И ДАННЫЕ ДЛЯ ОТЛАДКИ ******************************
;
; Отладочные сообщения
;
NO_COMMAND_msg db 'NO COMMAND',CR,LF,'$'
INIT_msg db 'INITialization',CR,LF,'$'
MEDIA_CHECK_msg db 'MEDIA Check',CR,LF,'$'
BUILD_BPB_msg db 'Build BIOS Parameter Block',CR,LF,'$'
IOCTL_INPUT_msg db 'IO Control Input',CR,LF,'$'
READ_msg db 'Input from Device',CR,LF,'$'
READ_NOWAIT_msg db 'Nondestructive Input no-wait',CR,LF,'$'
INPUT_STATUS_msg db 'Input Status',CR,LF,'$'
INPUT_FLUSH_msg db 'Flush Input Queue',CR,LF,'$'
WRITE_msg db 'Output to Device',CR,LF,'$'
WRITE_VERIFY_msg db 'Output with Verify',CR,LF,'$'
OUTPUT_STATUS_msg db 'Output Status',CR,LF,'$'
OUTPUT_FLUSH_msg db 'Flush Output Queue',CR,LF,'$'
IOCTL_OUTPUT_msg db 'IO Control Output',CR,LF,'$'
DEVICE_OPEN_msg db 'Open a Device',CR,LF,'$'
DEVICE_CLOSE_msg db 'Close a Device',CR,LF,'$'
REMOVABLE_msg db 'Is Media Removable',CR,LF,'$'
GENERIC_IOCTL_msg db 'Generic IOCTL Request',CR,LF,'$'
GET_LOGICAL_msg db 'Get Logical Device',CR,LF,'$'
SET_LOGICAL_msg db 'Set Logical Device',CR,LF,'$'
;
PAGE
;
; ============= ТАБЛИЦА АДРЕСОВ ОТЛАДОЧНЫХ СООБЩЕНИЙ =================
;
message_table LABEL WORD
dw offset INIT_msg ; 01 - инициализация
dw offset MEDIA_CHECK_msg ; 02 - проверка носителя
dw offset BUILD_BPB_msg ; 03 - построить BPB
dw offset IOCTL_INPUT_msg ; 04 - ввод IOCTL
dw offset READ_msg ; 05 - ввод из устройства
dw offset READ_NOWAIT_msg ; 06 - неразруш. ввод без ожид.
dw offset INPUT_STATUS_msg ; 07 - ввод статуса
dw offset INPUT_FLUSH_msg ; 08 - сброс входной очереди
dw offset WRITE_msg ; 09 - вывод на устройство
dw offset WRITE_VERIFY_msg ; 10 - вывод с проверкой
dw offset OUTPUT_STATUS_msg ; 11 - вывод статуса
dw offset OUTPUT_FLUSH_msg ; 12 - сброс выходной очереди
dw offset IOCTL_OUTPUT_msg ; 13 - вывод IOCTL
dw offset DEVICE_OPEN_msg ; 14 - открыть устройство
dw offset DEVICE_CLOSE_msg ; 15 - закрыть устройство
dw offset REMOVABLE_msg ; 16 - носитель сменный ?
dw offset NO_COMMAND_msg ; 17 -
dw offset NO_COMMAND_msg ; 18 -
dw offset NO_COMMAND_msg ; 19 -
dw offset GENERIC_IOCTL_msg ; 20 - групповой IOCTL запрос
dw offset NO_COMMAND_msg ; 21 -
dw offset NO_COMMAND_msg ; 22 -
dw offset NO_COMMAND_msg ; 23 -
dw offset GET_LOGICAL_msg ; 24 - получить имя диска
dw offset SET_LOGICAL_msg ; 25 - установить имя диска
;
PAGE
; PRINT_COMMAND
;
; Эта процедура вызывает функцию BIOS для печати (_biosprt), передавая
; ей адрес строки, содержащей имя только что вызванной команды. При
; вызове этой процедуры удвоенный код команды передается в регистре BX.
; Все используемые регистры сохраняются.
;
print_command PROC NEAR
push ax ; сохраним содержимое рег. AX
mov ax, BLUE_F OR BRIGHT OR BLACK_B ; установим цвет
push ax
mov ax,word ptr message_table[bx] ; адрес строки
push ax
call _biosprt ; вызываем процедуру BIOS
add sp,4 ; очищаем стек от параметров
pop ax ; восстанавливаем AX и выходим
ret
print_command ENDP
ENDIF
;
PAGE
;
; ******* ВНУТРЕННИЙ СТЕК И КОНЕЦ ОПЕРАЦИОННОЙ ЧАСТИ ДРАЙВЕРА ********
;
db 32 DUP ('stack ') ; внутренний стек глубиной
local_stack EQU $ ; 256 байт
;
bpb_tab dw offset bpb ; указатель на BPB
;
LAST_USED EQU $ ; адрес завершения
;
; ******* ХАРАКТЕРИСТИКИ RAM-ДИСКА, ПРИНИМАЕМЫЕ ПО УМОЛЧАНИЮ **********
;
; Параметры для 5-1/4" двустороннего двойной плотности диска с девятью
; секторами на дорожке.
;
MTYPE EQU 0FDh ; байт описателя носителя
TRACKS EQU 40 ; 40 дорожек
SECTORS EQU 9 ; 9 секторов на дорожке
DSIZE EQU 512 ; 512 байт в секторе
SIDES EQU 2 ; 2 стороны на диске
;
FSECS EQU 2 ; количество секторов в FAT
DIREN EQU 112 ; количество элементов директория
DSECS EQU 7 ; 7 секторов в директории
CLSIZ EQU 2 ; 2 сектора в кластере
;
STOTAL EQU TRACKS*SECTORS*SIDES ; всего секторов
PTOTAL EQU (DSIZE/16)*STOTAL ; всего параграфов
;
; ************ НАЧАЛО ОБЛАСТИ ДАННЫХ RAM-ДИСКА ***********************
;
; RAM-диск д.б. выровнен на границу параграфа
;
IF ($-ORIGIN) mod 16
ORG ($-ORIGIN) + 16 - (($-ORIGIN) mod 16)
ENDIF
RDISK LABEL BYTE ; начало RAM-диска
RPARA EQU ($-ORIGIN)/16 ; размер кода в параграфах
;
; ------------ Блок параметров BIOS ----------------------------------
;
jmp short boot ; короткий JMP (2 байта)
nop ; требуется для boot_record
db 'IBM 3.1' ; 8 байт имя и версия
;
bpb bpbstrc
dw SECTORS ; количество секторов на дорожке
dw SIDES ; количество головок чтения/записи
dw 0 ; количество скрытых секторов
boot:
db (DSIZE-30) DUP (?) ; остаток boot_sector
;
; ------------ Таблицы размещения файлов (FAT) -----------------------
; ; первые два элемента FAT
FAT_1 db MTYPE,0FFh,0FFh ; нулевой остаток FAT
db (DSIZE-3) DUP (0)
db ((FSECS-1) * DSIZE) DUP (0)
FAT_2 db MTYPE,0FFh,0FFh ; первые два элемента FAT
db (DSIZE-3) DUP (0) ; нулевой остаток FAT
db ((FSECS-1) * DSIZE) DUP (0)
;
; ------------ Сектора директория ------------------------------------
;
DIREC db 'RAM_DISK ' ; имя тома (11 байт)
db 08h ; VID
db 10 DUP (?) ; зарезервировано
dw 0600h ; время 12:00:00 (полдень)
dw 021h ; дата 1 января 1980 года
dw 0 ; начальный кластер 0
dd 0 ; размер файла 0
db (DSIZE-32) DUP (0) ; нулевой остаток директория
db ((DSECS-1) * DSIZE) DUP (0)
BUFFER LABEL BYTE ; начало области данных
;
; ************ ПРОЦЕДУРА ИНИЦИАЛИЗАЦИИ *******************************
;
INCLUDE stdmac.inc
;
; ============ Область данных инициализации ==========================
;
$signon db 'RAM DISK Driver Version 1.00 Installed: Drive'
$desig db 'A'
$crlf db 0Dh,0Ah,'$'
;
; ============ Начало процедуры инициализации ========================
;
INIT PROC NEAR ; 00 - инициализация
;
; установим адрес завершения, количество устройств и указатель на
; таблицу BPB
;
mov request.endadro,0 ; адрес конца драйвера
mov request.endadrs,cs
add request.endadrs,(RPARA+PTOTAL) ; последний параграф
mov request.units,1
mov request.bpbtabo,offset bpb_tab
mov request.bpbtabs,cs
mov al,$desig ; скорректируем имя диска
add al,request.devnum
mov $desig,al
;
; вывод на экран идентификационной строки
@DisStr $signon
;
; скорректируем значение "max_cmd" исходя из версии MS-DOS
@GetDOSVersion ; получим номер версии MS-DOS
cmp al,3 ; MS-DOS версии 3.00 и выше ?
jb init_done ; нет - прекращаем инициализацию
mov [max_cmd],CMD_PRE_32 ; команды для MS-DOS 3.00
cmp ah,2 ; MS-DOS версии 3.20 и выше ?
jb init_done ; нет - прекращаем инициализацию
mov [max_cmd],CMD_32 ; команды для MS-DOS 3.20
;
init_done:
xor ax,ax ; нет проблем !
ret
INIT ENDP
;
; ************ КОНЕЦ ДРАЙВЕРА. КОНЕЦ ФАЙЛА ***************************
;
_TEXT ENDS
END
---------------------------------------------------------------------
|