Пример драйвера блочного устройства
Приведем пример драйвера "электронного" диска, расположенного в основной (не расширенной или дополнительной) памяти компьютера. Этот драйвер предназначен, разумеется, не для замены поставляющегося стандартно RAMDRIVE.SYS, однако на его примере можно увидеть, как устроены драйверы блочных устройств.
И если когда-нибудь Вам потребуется использовать диски ЭВМ серии ЕС в качестве винчестера персонального компьютера, то разобравшись в том, как работает приведенный ниже драйвер, Вы сможете самостоятельно приспособить его для такой задачи.
; ; Драйвер электронного диска, ; использует основную память компьютера ; .MODEL tiny .CODE ; Драйвер состоит из одного ; сегмента кода
org 0 ; Эта строка может отсутствовать
include sysp.inc
ram PROC far
;=======================================================
; Заголовок драйвера
dd 0ffffffffh ;адрес следующего драйвера dw 2000h ;байт атрибутов dw dev_strategy ;адрес процедуры стратегии dw dev_interrupt ;адрес процедуры прерывания db 1 db 7 dup(?)
; Блок BPB для электронного диска
bpb equ $
dw 512 ; количество байтов в секторе db 1 ; количество секторов в кластере dw 1 ; количество зарезервированных секторов db 2 ; количество копий FAT dw 64 ; макс. количество файлов в корневом каталоге dw 360 ; общее количество секторов db 0fch ; описатель среды носителя данных dw 2 ; количество секторов на одну копию FAT
bpb_ptr dw bpb ; указатель на блок BPB
; Область локальных переменных драйвера
total dw ? ; количество секторов verify db 0 ; флаг проверки при записи start_sec dw 0 ; номер начального сектора vdisk_ptr dw 0 ; сегмент начала участка памяти, ; в котором расположен диск
user_dta dw ? ; адрес области передачи данных dw ?
; Образец записи BOOT для инициализации ; первого сектора диска
boot_rec equ $
db 3 dup(0) db 'MSDOS4.0' dw 512 db 1 dw 1 db 2 dw 64 dw 360 db 0fch dw 2
;========================================================
; Программа стратегии
dev_strategy: mov cs:req_seg,es mov cs:req_off,bx ret
; Здесь запоминается адрес заголовка запроса
req_seg dw ? req_off dw ?
;=======================================================
;Обработчик прерывания
dev_interrupt: push es ; сохраняем регистры push ds push ax push bx push cx push dx push si push di push bp
; Устанавливаем ES:BX на заголовок запроса
mov ax,cs:req_seg mov es,ax mov bx,cs:req_off
; Получаем код команды из заголовка запроса и умножаем ; его на два, чтобы использовать в качестве индекса ; таблицы адресов обработчиков команд
mov al,es:[bx]+2 shl al,1
sub ah,ah ; Обнуляем AH lea di,functions ; DI указывает на смещение ; таблицы add di,ax ; Добавляем смещение в таблице jmp word ptr [di] ; Переходим на адрес из таблицы
functions LABEL WORD ; Таблица функций
dw initialize dw check_media dw make_bpb dw ioctl_in dw input_data dw nondestruct_in dw input_status dw clear_input dw output_data dw output_verify dw output_status dw clear_output dw ioctl_out dw Device_open dw Device_close dw Removable_media
; Выход из драйвера, если функция не поддерживается
ioctl_in: nondestruct_in: input_status: clear_input: output_status: clear_output: ioctl_out: Removable_media: Device_open: Device_close:
or es:word ptr [bx]+3,8103h jmp quit
;=======================================================
; Построение блока BPB
make_bpb:
push es push bx
mov cs:WORD PTR start_sec,0 mov cs:WORD PTR total,1 call calc_adr
push cs pop es
lea di,bpb add si,11 mov cx,13 rep movsb
pop bx pop es
lea dx,bpb mov es:18[bx],dx mov es:20[bx],cs
jmp quit
check_media:
; Проверка смены носителя данных. ; Носитель не менялся.
mov es:BYTE PTR 14[bx],1 jmp quit
; Обработчик команды вывода данных
output_verify:
; Для вывода с проверкой устанавливаем флаг проверки
mov cs:BYTE PTR verify,1
output_data:
call in_save mov ax,es:WORD PTR 20[bx] mov cs:start_sec,ax
mov ax,es:WORD PTR 18[bx] mov cs:total,ax
call sector_write
mov es,cs:req_seg mov bx,cs:req_off
cmp cs:BYTE PTR verify,0 jz no_verify
mov cs:BYTE PTR verify,0 jmp input_data
no_verify:
jmp quit
;=======================================================
; Обработчик команды ввода данных
input_data:
call in_save mov ax,es:WORD PTR 20[bx] mov cs:start_sec,ax
mov ax,es:WORD PTR 18[bx] mov cs:total,ax
call sector_read
mov es,cs:req_seg mov bx,cs:req_off
jmp quit
;========================================================
quit: or es:word ptr [bx]+3, 100h pop bp pop di pop si pop dx pop cx pop bx pop ax pop ds pop es ret
;========================================================
; Процедура выводит на экран строку ; символов в формате ASCIIZ
dpc proc near push si dpc_loop: cmp ds:byte ptr [si],0 jz end_dpc mov al,ds:byte ptr [si] @@out_ch al inc si jmp dpc_loop
end_dpc: pop si ret dpc endp
;========================================================
hello db 13,10,'++' db 13,10,'¦ *RAM/DISK* (C)Frolov A., 1990 ¦' db 13,10,'++' db 13,10,0
;========================================================
; Сохранение адреса буфера и значения счетчика ; из области запроса в области локальных данных
in_save proc near
mov ax,es:WORD PTR 14[bx] mov cs:user_dta,ax
mov ax,es:WORD PTR 16[bx] mov cs:user_dta+2,ax
mov ax,es:WORD PTR 18[bx] xor ah,ah mov cs:total,ax
ret
in_save endp
; Процедура пересчитывает адрес сектора ; в адрес соответствующего этому сектору ; блока памяти. В регистре DS возвращается ; сегментный адрес этого блока, ; в CX - общее количество байт во всех секторах. ; Количество секторов задается в total, ; номер начального сектора - в start_sec
calc_adr proc near
mov ax,cs:start_sec mov cx,20h mul cx
mov dx,cs:vdisk_ptr add dx,ax mov ds,dx
xor si,si mov ax,cs:total mov cx,512 mul cx
or ax,ax jnz move_it
mov ax,0ffffh
move_it:
xchg cx,ax ret
calc_adr endp
; Чтение сектора из памяти виртуального диска
sector_read proc near
call calc_adr mov es,cs:user_dta+2 mov di,cs:user_dta
mov ax,di add ax,cx jnc read_copy mov ax,0ffffh sub ax,di mov cx,ax read_copy: rep movsb ret
sector_read endp
; Запись сектора в память виртуального диска
sector_write proc near
call calc_adr push ds pop es mov di,si mov ds,cs:user_dta+2 mov si,cs:user_dta
mov ax,si add ax,cx jnc write_copy mov ax,0ffffh sub ax,si mov cx,ax write_copy: rep movsb ret
sector_write endp
;========================================================
E_O_P: ;Метка конца программы
;========================================================
initialize:
push cs pop dx
lea ax,cs:vdisk ; начало памяти, в которой ; расположен диск mov cl,4 ror ax,cl add dx,ax mov cs:vdisk_ptr,dx
mov ax,2d00h ; размер памяти, отведенной ; для диска add dx,ax
; Записываем в область запроса адрес за ; концом области памяти, отведенной диску
mov es:word ptr [bx]+14,0 mov es:word ptr [bx]+16,dx
; Количество поддерживаемых логических дисков - 1
mov es:word ptr [bx]+13,1
; Возвращаем адрес построенного BPB
lea dx,bpb_ptr mov es:word ptr [bx]+18,dx mov es:word ptr [bx]+20,cs
; Инициализируем BOOT-сектор
mov es,cs:vdisk_ptr xor di,di lea si,boot_rec mov cx,24 rep movsb
; Обнуляем два сектора для FAT
mov cs:WORD PTR start_sec,1 mov cs:WORD PTR total,2 call calc_adr
push ds pop es mov di,si xor al,al rep stosb
; Подготавливаем первую копию FAT
mov ds:BYTE PTR [si],0fch mov ds:BYTE PTR 1[si],0ffh mov ds:BYTE PTR 2[si],0ffh
; Подготавливаем вторую копию FAT
push ds push si
mov cs:WORD PTR start_sec,3 mov cs:WORD PTR total,2 call calc_adr
push ds pop es mov di,si
pop si pop ds
rep movsb
; Записываем нули в сектора корневого каталога
mov cs:WORD PTR start_sec,5 mov cs:WORD PTR total,4 call calc_adr
xor al,al push ds pop es xor di,di rep stosb
; Выводим сообщение
mov ax,cs mov ds,ax mov si,offset hello call dpc
jmp quit
; Здесь начинается область данных, в которой ; расположен электронный диск. Эта область ; выровнена на границу параграфа.
ALIGN 16
vdisk equ $ ram ENDP END ram