; Демонстрационный драйвер символьного устройства
;
; (C) Фролов А.В., Фролов Г.В., 1995

  .MODEL tiny
  .CODE
  ORG 0

@@out_ch MACRO c1,c2,c3,c4,c5,c6,c7,c8,c9,c10
  mov  ah,02h
  IRP  chr,<c1,c2,c3,c4,c5,c6,c7,c8,c9,c10>
    IFB  <chr>
      EXITM
    ENDIF
    mov  dl,chr
    int  21h
  ENDM
ENDM

@@out_str MACRO
  mov   ah,9
  int   21h
ENDM

devdrv proc far

  dd   0ffffffffh      ; адрес следующего драйвера
  dw   0C800h          ; байт атрибутов
  dw   dev_strategy    ; адрес процедуры стратегии
  dw   dev_interrupt   ; адрес процедуры прерывания
  db   'DEVDRIVR'      ; имя устройства 

;===================================================
; Программа стратегии
;===================================================

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

  mov  ax, cs:req_seg
  mov  es, ax
  mov  bx, cs:req_off
  mov  al, es:[bx]+2
  shl  al, 1
  sub  ah, ah
  lea  di, functions    
  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
  dw   reserved
  dw   reserved
  dw   reserved
  dw   generic_ioctl
  
check_media:
make_bpb:
clear_input:
output_verify:
clear_output:
Removable_media:
reserved:
generic_ioctl:

; Выводим сообщение о том, что вызвана
; команда, которая не поддерживается драйвером

  mov ax, cs
  mov ds, ax
  mov si, offset errmsg
  call dpc

; Ожидаем, пока пользователь нажмет 
; любую клавишу

  mov ax, 0
  int 16h

; Устанавливаем признак ошибки

  or   es:word ptr [bx]+3, 8103h
  jmp  quit

;==================================================
nondestruct_in:

; Выводим сообщение о начале неразрушающего ввода

  mov ax, cs
  mov ds, ax
  mov si, offset inpmsg_nd
  call dpc

; Вводим символ с клавиатуры и помещаем
; его в область запроса

  mov  ax, 0
  int  16h
  mov  BYTE PTR es:[bx]+0dh, al

  jmp  quit

;===================================================
input_status:

; Выводим сообщение о вызове команды
; проверки состояния ввода

  mov ax, cs
  mov ds, ax
  mov si, offset statmsg_i
  call dpc

; Устанавливаем признак "Занято", так как
; последующая команда чтения приведет к ожиданию
; (буферизация не используется)

  or   es:word ptr [bx]+3, 0200h
  jmp  quit

;===================================================
output_status:

; Выводим сообщение о вызове команды
; проверки состояния вывода

  mov ax, cs
  mov ds, ax
  mov si, offset statmsg_o
  call dpc

; Бит занятости не устанавливаем, так как
; считаем, что консоль доступна для вывода

  or   es:word ptr [bx]+3, 0000h
  jmp  quit

;===================================================
; Обработчик команды вывода данных

output_data:

; Записываем в регистр CL количество
; символов, которые будут выведены

  mov  cl, es:[bx]+18
  push cx

; Выводим сообщение о начале вывода

  mov ax, cs
  mov ds, ax
  mov si, offset outmsg
  call dpc

  pop cx

; Загружаем в DS:SI адрес буфера данных

  mov  ax, es:[bx]+16
  mov  ds, ax
  mov  si, es:[bx]+14

; Выводим на экран символы из буфера

out_loop:
  mov  al, ds:byte ptr [si]
  @@out_ch al
  inc si
  loop out_loop

  jmp  quit

;===================================================
; Обработчик команды ввода данных

input_data:

; Записываем в регистр CL количество
; символов, которые будут введены

  mov  cl, es:[bx]+18
  push cx

; Выводим сообщение о начале ввода

  mov ax, cs
  mov ds, ax
  mov si, offset inpmsg
  call dpc

; Загружаем в DS:SI адрес буфера данных

  pop  cx
  mov  ax, es:[bx]+16
  mov  ds, ax
  mov  di, es:[bx]+14

; Вводим символы с клавиатуры и записываем в буфер

inp_loop:
  mov  ax, 0
  int  16h
  mov  ds:byte ptr [di], al
  @@out_ch al
  inc di
  loop inp_loop

  jmp  quit

;===================================================
; Обработчик команды вывода данных IOCTL

ioctl_out:

; Записываем в регистр CL количество
; символов, которые будут вывдедены

  mov  cl ,es:[bx]+18

; Загружаем в DS:SI адрес буфера данных

  mov  ax, es:[bx]+16
  mov  ds, ax
  mov  si, es:[bx]+14

; Выводим на экран символы из буфера

ioctl_out_loop:
  mov  al, ds:byte ptr [si]
  @@out_ch al
  inc si
  loop ioctl_out_loop

  jmp  quit

;===================================================
; Обработчик команды ввода данных IOCTL

ioctl_in:

; Записываем в регистр CL количество
; символов, которые будут введены

  mov  cl, es:[bx]+18

; Загружаем в DS:SI адрес буфера данных

  mov  ax, es:[bx]+16
  mov  ds, ax
  mov  di, es:[bx]+14

; Вводим символы с клавиатуры и записываем в буфер

ioctl_inp_loop:
  mov  ax, 0
  int  16h
  mov  ds:byte ptr [di], al
  @@out_ch al
  inc di
  loop ioctl_inp_loop

  jmp  quit

;===================================================
Device_open:

; Выводим сообщение об открытии устройства

  mov ax, cs
  mov ds, ax
  mov si, offset openmsg
  call dpc

  jmp quit

;===================================================
Device_close:

; Выводим сообщение о закрытии устройства

  mov ax, cs
  mov ds, ax
  mov si, offset closemsg
  call dpc

  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

;===================================================
parm_off dw ? ; смещение строки параметров
parm_seg dw ? ; сегмент строки параметров

pc_type  dw ? ; область памяти для сохранения
int_num  dw ? ;   значений параметров
out_port dw ?
inp_port dw ?
ctrl_inp_port dw ?
ctrl_out_port dw ?

;===================================================
;  Имя        interrupt
;             Обработчик прерывания INT <nn>
;
;  Вход:  nn - Номер прерывания, заданный в файле
;              config.sys
;         AH - Номер выполняемой функции:
;                0 - операция записи;
;                1 - операция чтения
;         BH - Адрес (0...7Fh)
;         BL - Данные для записи (0...FFh)
;  Выход: BL - Прочитанные данные
;
; Описание
;
;     Прерывание вызывается командой INT <nn> с
;         параметрами:
;
; Вход:  nn - Номер прерывания, заданный в файле 
;             config.sys
;        AH - Номер выполняемой функции:
;               0 - операция записи;
;               1 - операция чтения
;        BH - Адрес (0...7Fh)
;        BL - Данные для записи (0...FFh)
; Выход: BL - Прочитанные данные
;
; Возвращаемое значение
; BL - Прочитанные данные
;===================================================

interrupt:
  push ax
  push cx
  push dx
  push bp
  push si
  push di
  push ds
  push es

  cmp ah, 0      ; команда записи
  jz int_write
  cmp ah, 1      ; команда чтения
  jz int_read

; Обработка неизвестной команды

  @@out_ch 13,10,'?','?','?',13,10

; Устанавливаем признак ошибки

  mov ax, 0ffffh
  jmp int_exit

int_write:

; Выводим сообщение о приходе прерывания,
; предназначенного для записи

  @@out_ch 13,10,'W','R','I','T','E',13,10

  mov ax, 0
  jmp int_exit

int_read:

; Выводим сообщение о приходе прерывания,
; предназначенного для чтения

  @@out_ch 13,10,'R','E','A','D',13,10

; Имитация чтения, всегда возвращается значение 55h

  mov bl, 55h
  mov ax, 0
  jmp int_exit

int_exit:
  pop es
  pop ds
  pop di
  pop si
  pop bp
  pop dx
  pop cx
  pop ax

  iret

;===================================================
; Процедура выводит на экран строку
; символов в формате 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,'|  *DEVDRV* (C)Frolov A., 1995   |'
      db 13,10,'+--------------------------------+'
      db 13,10,0

outmsg   db 13,10,'Вывод на устройство ',0
inpmsg   db 13,10,'Ввод с устройства ',0

openmsg  db 13,10,'Открываем DEVDRIVR',0
closemsg db 13,10,'Закрываем DEVDRIVR',0

inpmsg_nd db 13,10
  db 'Неразрушающий ввод с устройства',0
statmsg_i db 13,10
  db 'Чтение состояния ввода ',0
statmsg_o db 13,10
  db 'Чтение состояния вывода ',0
errmsg  db 13,10
  db 'Команда не поддерживается ',0

;===================================================

E_O_P:
initialize:

  lea  ax, E_O_P
  mov  es:word ptr [bx]+14, ax 
  mov  es:word ptr [bx]+16, cs

; Смещение и сегмент строки параметров  

  mov  ax, es:word ptr [bx]+18
  mov  cs:parm_off, ax
  mov  ax, es:word ptr [bx]+20
  mov  cs:parm_seg, ax

; Стираем экран

  mov   dh, 18h
  mov   dl, 80h
  xor   cx, cx
  mov   bh, 7
  xor   al, al
  mov   ah, 6
  int   10h

; Устанавливаем курсор в левый верхний угол экрана

  mov   bh, 0
  xor   dx, dx
  mov   ah, 2
  int   10h

; Выводим сообщение

  mov ax, cs
  mov ds, ax
  mov si, offset hello
  call dpc

; Раскодируем строку и проверяем
; корректность заданных параметров

  push cs
  pop  ds

; ES:BX - адрес строки параметров
  
  mov  bx, cs:parm_off   
  mov  ax, cs:parm_seg
  mov  es, ax

; Адрес начала области параметров

  mov  bp, OFFSET cs:pc_type

; Анализируем параметры

  call parm
  jc   parm_errors

; Устанавливаем вектор прерывания с номером,
; заданным в строке параметров

  push cs
  pop  ds
  mov  dx, OFFSET cs:interrupt
  mov  ax, cs:int_num
  mov  ah, 25h
  int  21h

  jmp  quit

parm_errors:

; Если параметры заданы с ошибкой,
; установку драйвера не выполняем.
; ES:BX указывают на заголовок запроса

  mov  ax, cs:req_seg 
  mov  es, ax
  mov  bx, cs:req_off

  lea  ax, devdrv  
  mov  es:word ptr [bx]+14, ax
  mov  es:word ptr [bx]+16, cs

  jmp quit

;===================================================
; Разбор строки параметров

parm proc near
  xor  si, si ; индекс в строке параметров

next_chr: 
  mov  al, BYTE PTR es:[bx][si]
  cmp  al, 0ah  ; проверки на конец
  je   parm_br  ; строки параметров
  cmp  al, 0dh
  je   parm_br
  cmp  al, 0h
  jz   parm_br

; Копируем очередной байт строки параметров в буфер

  mov  BYTE PTR cs:parm_buffer[si], al
  inc  si
  jmp  next_chr

; Закрываем скопированную строку параметров нулем

parm_br:  
  
  mov   BYTE PTR cs:parm_buffer[si], 0

; Подготавливаем регистры для вызова программы
; анализа параметров и проверяем правильность
; заданных параметров

  mov   dx, OFFSET sep
  mov   cl, 6
  mov   bx, OFFSET cs:parm_buffer

  call  get_parm
  jc    err_msg

  mov   ax,cs:pc_type
  cmp   ax, 0
  jz    model_is_valid
  cmp   ax, 1
  jz    model_is_valid

  jmp   err_msg

model_is_valid:

; Если параметры заданы правильно,
; выводим их значения на экран

  mov   si, OFFSET msg1
  call  dpc
  mov   ax, cs:pc_type
  call  print_word

  mov   si, OFFSET msg2
  call  dpc
  mov   ax, cs:int_num
  call  print_word

  mov   si, OFFSET msg3
  call  dpc
  mov   ax, cs:out_port
  call  print_word

  mov   si, OFFSET msg4
  call  dpc
  mov   ax, cs:inp_port
  call  print_word

  mov   si, OFFSET msg41
  call  dpc
  mov   ax, cs:ctrl_inp_port
  call  print_word

  mov   si, OFFSET msg42
  call  dpc
  mov   ax, cs:ctrl_out_port
  call  print_word

  @@out_ch  13,10,13,10
  clc
  jmp   end_of_parm
err_msg:

; Если были ошибки в параметрах, выводим
; сообщение об ошибке,
; саму ошибочную строку параметров
; и ожидаем, пока пользователь не нажмет 
; на любую клавишу.
; На выходе устанавливаем флаг CARRY

  mov   si, OFFSET msg5
  call  dpc

  mov   ds, cs:parm_seg
  mov   si, cs:parm_off
  call  dpline

  mov   ax, cs
  mov   ds, ax
  mov   si, OFFSET msg6
  call  dpc

  mov   ax, 0
  int   16h
  stc
end_of_parm:  ret

parm_buffer db  100 DUP (?)

sep   db " ",0
msg1  db 13,10,"PC Type                 ........ ",0
msg2  db 13,10,"Used Interrupt Number   ........ ",0
msg3  db 13,10,"Device Input Port       ........ ",0
msg4  db 13,10,"Device Output Port      ........ ",0
msg41 db 13,10,"Device Inp Control Port ........ ",0
msg42 db 13,10,"Device Out Control Port ........ ",0

msg5  db 13,10,"Invalid Driver Parameter!",13,10,0

msg6  db 13,10,13,10,"  Press any key...",13,10,0

parm endp

; get_parm
; Разбор строки параметров.
;
;       ds:bx  -  исходная строка, закрытая нулем
;       ds:dx  -  строка разделительных символов,
;                 закрытая нулем
;       ds:bp  -  буфер параметров
;          cx  -  количество параметров
;
; Разбирается исходная строка ds:bx, разделенная
; символами-сепараторами. Адрес строки,  
; содержащей сепараторы, находится в регистрах ds:dx.
; Количество параметров в исходной строке
; находится в регистре cx.
; Строка параметров начинается с полного пути 
; к файлу, содержащему драйвер.
; Параметры заданы шестнадцатеричными цифрами. 
; Двоичные слова, соответствующие параметрам, 
; последовательно записываются в буфер параметров, 
; расположенный по адресу ds:bp
;
; В случае ошибки устанавливается флаг  переноса

get_parm proc near

  push  bx
  push  cx
  push  ax
  push  si

  xor   ch, ch
  xor   ax, ax
  mov   si, ax

  call  strtoc
  jc    parm_end

parm_loop:
  
  mov   ax, 22h
  call  strtoc
  jc    parm_end
  call  hex_to_bin
  jc    parm_end
  mov   ds:[bp][si], ax
  inc   si
  inc   si
  loop  parm_loop

parm_end:
		
  pop   si
  pop   ax
  pop   cx
  pop   bx

  ret

get_parm endp

; strtoc
; Выделение очередного слова из строки
;
; При первом обращении к процедуре:
;   Вход:
;     ax = 0
;     ds:bx  -  исходная строка, закрытая нулем
;     ds:dx  -  строка сепараторов, закрытая нулем
;   Выход:
;     ds:bx  -  подстрока до первого разделителя,
;               закрытая нулем
;
; При последующих обращениях
;   Вход:
;     ax != 0
;     ds:dx  -  строка из сепараторов, закрытая
		нулем
;   Выход:
;     ds:bx  -  подстрока до следующего
;               разделителя, закрытая нулем
;
;  При первом вызове функция выделяет из строки первое
;  слово до разделителя. При повторном вызове
;  возвращает указатель на следующее слово в
;  исходной строке. Если все слова из строки
;  уже выделены, устанавливается флаг переноса.

strtoc proc near

  push  bp
  push  di

  mov   space, 0

  cmp   ax, 0
  jz    first

  mov   bx, cs:ds1_off
  mov   ax, cs:ds1_seg
  mov   ds, ax

first:    
  
  cmp   BYTE PTR ds:[bx], 0
  jz    error

  mov   bp, bx

str_begin:  

  mov   di, dx

compe:    

  mov   ah, BYTE PTR ds:[bp]
  cmp   ah, 0
  jz    lab
  cmp   BYTE PTR ds:[di], ah
  jne   next
  mov   BYTE PTR ds:[bp], 0
  inc   bp
  inc   BYTE PTR cs:space
  jmp   str_begin

lab:      

  mov   WORD PTR cs:ds1_off, bp
  mov   ax, ds
  mov   WORD PTR cs:ds1_seg, ax
  jmp   end_proc

next:     
  
  inc   di
  cmp   BYTE PTR ds:[di], 0
  jnz   compe
  cmp   BYTE PTR cs:space, 0
  jnz   lab
  inc   bp
  jmp   str_begin

error:    
  
  stc

end_proc: 

  pop   di
  pop   bp
  ret

ds1_off dw ?
ds1_seg dw ?
space   db ?

strtoc endp

; hex_to_bin
; Преобразует шестнадцатеричную строку 
; в двоичное число
;
; Вход:
;    ds:bx  -  исходная строка, закрытая нулем
; Выход:
;    ax     -  результат преобразования
;
; Функция преобразует строку из ascii-символов в 
; шестнадцатеричном формате в 16-битовый эквивалент.
; В случае переполнения или если строка
; содержит символы, отличные от 0..9, A..F, a..f,
; устанавливается флаг переноса

hex_to_bin PROC NEAR

  push  bp
  push  si
  push  dx

  xor   ax, ax
  mov   bp, bx

begin:    

  cmp   BYTE PTR ds:[bp], 0h
  jz    end_pro
  call  hex
  jc    h_error

  mov   si, 10h
  xor   dx, dx
  mul   si
  cmp   dx, 0
  jnz   h_error

  add   ax, bx
  inc   bp
  jmp   begin

h_error:  
  
  stc

end_pro:
  
  pop  dx
  pop  si
  pop  bp
  ret
hex_to_bin endp

hex proc near
  xor   bh, bh
  mov   bl, BYTE PTR ds:[bp]

  cmp   bl, '0'
  jb    hex_error
  cmp   bl, '9'
  ja    next_big
  and   bx, 0fh
  jmp   ok

next_big: 

  cmp   bl, 'A'
  jb    hex_error
  cmp   bl, 'F'
  ja    next_small
  sub   bl, 37h
  jmp   ok

next_small: 
  
  cmp   bl, 'a'
  jb    hex_error
  cmp   bl, 'f'
  ja    hex_error
  sub   bl, 57h
  jmp   ok

hex_error:  

  stc

ok:         
  
  ret

hex endp

; dec_to_bin
; Преобразует  десятичную строку в двоичное число
;
; Вход:
;    ds:bx  -  исходная строка, закрытая нулем
; Выход:
;    ax     -  результат преобразования

dec_to_bin proc near

  push  bp
  push  si
  push  dx

  xor   ax, ax
  mov   bp, bx

d_begin:  

  cmp   BYTE PTR ds:[bp], 0h
  jz    d_end_pro

  cmp   BYTE PTR ds:[bp], '0'
  jb    d_error
  cmp   BYTE PTR ds:[bp], '9'
  ja    d_error
  mov   si, 10
  xor   dx, dx
  mul   si
  cmp   dx, 0
  jnz   d_error
  mov   bl, BYTE PTR ds:[bp]
  and   bx, 0fh
  add   ax, bx
  inc   bp
  jmp   d_begin

d_error:    

  stc

d_end_pro:
  pop  dx
  pop  si
  pop  bp
  ret

dec_to_bin endp

print_word proc near
  push ax
  push bx
  push dx

  push ax
  mov  cl, 8
  rol  ax, cl
  call byte_to_hex
  mov  bx, dx
  @@out_ch bh
  @@out_ch bl

  pop  ax
  call byte_to_hex
  mov  bx, dx
  @@out_ch bh
  @@out_ch bl

  pop dx
  pop bx
  pop ax
  ret
print_word endp

byte_to_hex proc near
  push ds
  push cx
  push bx

  lea  bx, tabl
  mov  dx, cs
  mov  ds, dx

  push ax
  and  al, 0fh
  xlat
  mov  dl, al

  pop ax
  mov  cl, 4
  shr  al, cl
  xlat
  mov  dh, al
  
  pop  bx
  pop  cx
  pop  ds
  ret

tabl db '0123456789ABCDEF'
byte_to_hex endp

dpline proc near
  push si
dpline_loop:
  cmp  ds:byte ptr [si],0dh
  jz   end_dpline
  cmp  ds:byte ptr [si],0ah
  jz   end_dpline

  mov  al,ds:byte ptr [si]
  @@out_ch al
  inc si
  jmp dpline_loop

end_dpline:
  pop si
  ret
dpline endp

devdrv  ENDP

  END devdrv

