;*******************************************************
;*                                                     *
;*     Asynchronous communication driver               *
;*           (for null modem only)                     *
;*                                                     *
;*  CopyRight (C) by Nicholas Poljakov. 1995.          *
;*                                                     *
;*******************************************************
.MODEL SMALL

 include i_s.inc
 include c:\m61\include\dos.inc

public	inbuff, in_cnt, sw_buff, err_cnt, open_type, b_size
public	out_buff, out_cnt, out_full, dcl1, dcl2, err1, lock_send, rcv_exit
public	rcv_off, rcv_seg, base_addr, port_num, save0b, save0c, tsrsize, my_psp
public	blocklt, startblock, ascom, quickexit, nextp, cnt01, fex, cnt00
public	ac_open, ac_opn_02, ac_opn_01, ac_opn_1, ac_opn_5, ac_opn_2, ac_opn_3
public	ac_opn_4, ac_opn_6, ac_opn_7, setcom1, setcom, ac_opn_retry, ac_opn_8
public	ac_opn_9, ac_opn_10, ac_opn_100, ac_opn_101, ac_mdm_act, ac_opn_11
public	ac_exit, ac_opn_exit, ac_close, ac_cls_01, ac_cls_02, ac_cls_03
public	ac_cls_0, ac_cls_exit, ac_send, sndproc, send_cycl, snd_retry, sndexit
public	sendal, send_cycxx, snd_retryx, int6e, int6e_start, popen, int6e_c0
public	int6e_c1, int6e_c2, int6e_c3, int6e_c4, ctl_send, when_ready5, int6e_c5
public	int6e_exit, io_int, next_int, testnext, receive, z_exit, savelt
public	rcvtest0, rcvcycl, rcvtest, callc, io_int_exit, int_exit


.STACK 10h

.DATA

inbuff        db 2048 dup (?)
in_cnt        dw 0      ;count of received bytes
sw_buff       db 0
err_cnt       db 0
open_type     db 0      ; 50h - open for PORT
b_size        db 0      ; size of block for transmition
out_buff      dd 0      ; out buffer address
out_cnt       dw 0      ; count of bytes that was send
out_full      dw 0      ; total of send bytes1
dcl1          db 0ah, 0dh
	      db 'Async. driver was already installed...$'
dcl2          db 0ah, 0dh
	      db 'Async. driver is installed.$'
err1          DB 13,10,"Must have DOS 3.0 or higher",13,10,"$"
lock_send     db 0
rcv_exit      dd 0
              org rcv_exit
rcv_off       dw ?
rcv_seg       dw ?
base_addr     dw 0
port_num      db 0
save0b        db 4 dup (?)
save0c        db 4 dup (?)
tsrsize       dw 0
my_psp        dw 0
BlockLT       dw 0
StartBlock    db 0

.CODE
;
ascom PROC
;********************************************************
;*                                                      *
;*              ASync. COMmunication                    *
;*                                                      *
;*  Function: Installation of 6E interrupt handler.     *
;*            The int 6e routine was disigned to support*
;*            the COM1 and COM2.                        *
;*                                                      *
;********************************************************
      mov  ax, _DATA
      mov  ds, ax

;
; Calculate Tsrsize
;
        mov   bx, ss
        mov   ax, es      ; PSP address
        mov   my_psp, ax
        sub   bx, ax
        mov   ax, sp
        mov   cx, 4
        shr   ax, cl      ; convert to paragraphs
        add   bx, ax
        inc   bx          ; one extra paragraph
        mov   tsrsize, bx
;
        @GetVer           ; Get DOS version
        cmp     al,3      ; Requires DOS 3.0
        jge     NextP
        @ShowStr err1     ; else error and quit
QuickExit:
        @Exit
;
NextP:

;*
;* Test if int6e also exist
;*

      mov  ax,356eh
      int  21h
      push es
      pop  ax
      cmp  ax,0
      je   cnt00
cnt01:
      mov  ah,3                ; check function
      int  6eh
      cmp  al,55h              ; is the int6e routine active ?
      jne  cnt00               ; no...
fex:
      mov  dx, offset dcl1
      mov  ah, 9
      int  21h
      jmp  QuickExit
cnt00:
      push ds
      mov  ax, _DATA
      mov  bx, 0ah
      mov  dx, seg int6e
      mov  ds, dx
      mov  word ptr [bx], ax   ; save ds for int6e routine
;
      mov  dx, seg io_int
      mov  ds, dx
      mov  bx, 0ah
      mov  word ptr [bx], ax   ; save ds for io_int routine
      pop  ds
;
      push ds                  ;сохраняем ds
      mov  dx,offset int6e     ;ds:dx указывают на процедуру
      mov  ax,seg int6e        ;
      mov  ds,ax               ;
      mov  al,6eh              ;номер вектора для com1
      mov  ah,25h              ;функция изменения вектора
      int  21h                 ;меняем вектор прерывания
      pop  ds
;
      mov  dx, offset dcl2
      mov  ah, 9
      int 21h
;
      mov   ax, my_psp
      mov   es, ax
      mov   ax, word ptr es:[2ch]
      mov   es, ax
      mov   ax, 4900h
      int   21h           ; Free the local environment

      mov  dx, tsrsize
      mov  ax,3100h
      int  21h
;

ascom ENDP

;*****************************************************
;*                                                   *
;*               O P E N  function                   *
;*                                                   *
;*   Input  : in AH - function code.                 *
;*            in AL - subfunction code               *
;*   Output : Return code. ( 0 - OK; -1 - error)     *
;*****************************************************

ac_open PROC
;*
;*  ES:DX -> init_serial structure
;*
      mov  bx,dx
;*    Save rcv_exit subroutine entry
      mov  ax, word ptr es:[bx + asc_rcv_exit]
      mov  rcv_off, ax
      mov  ax, word ptr es:[bx + asc_rcv_exit + 2]
      mov  rcv_seg, ax
;
;---получаем базовый адрес com#
      mov  al, byte ptr es:[bx + asc_parity]
      and  al, 0f0h        ; clear right bits
      mov  cl, 4
      ror  al, cl          ; first four bits is com. port number
      mov  port_num, al    ; save com. port number
      cmp  al, 2           ; high COM number (with int support)
      ja   ac_opn_02
      dec  al              ; com. num - 1
      rol  al, 1           ; (com - 1) * 2 - it's displacement in BIOS
      mov  si, 0
      and  ax, 00ffh
      add  si, ax
      push es
      mov  ax,40h          ;es указывает на область данных bios
      mov  es,ax           ;
      mov  dx,es:[si]      ;получаем базовый адрес com1
      pop  es
      mov  base_addr, dx
      cmp  dx, 0
      jne  ac_opn_01
ac_opn_02:
      mov  ax, 0ffffh      ; rc = -1 port not present
      jmp  ac_opn_exit
ac_opn_01:
;---инициализируеи регистры делителя скорости обмена
      add  dx,3            ;указываем на регистр контроля линии
      in   al, dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      mov  al,10000000b    ;устанавливаем бит 7
      out  dx,al           ;посылаем байт
      mov  ax, word ptr es:[bx + asc_speed]
      mov  cx, ax
      dec  dx              ;указываем на старший байт делителя
      dec  dx              ;скорости обмена
      in   al, dx
      mov  ax, cx          ; restory ax
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      mov  al, ah
      out  dx,al           ;посылаем старший байт делителя
      dec  dx              ;указываем на младший байт делителя
      in   al, dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      mov  ax, cx          ; restory ax
      out  dx,al           ;посылаем младший байт делителя
;
      cmp  open_type, 50h
      je   ac_opn_2   ; 8 bits in word
      mov  al, byte ptr es:[bx + asc_width]
      cmp  al, 5
      jb   ac_opn_1
      cmp  al,8
      ja   ac_opn_2
      jmp  ac_opn_3
ac_opn_1:
      mov  al, 5
      jmp  ac_opn_3
ac_opn_5:
      mov  ah, 2
      jmp  ac_opn_4
ac_opn_2:
      mov  al, 8
ac_opn_3:
      sub  al, 5
;---инициализируем регистр контроля линии
      mov  ah, byte ptr es:[bx + asc_stops]
      cmp  ah, 2
      ja   ac_opn_5
ac_opn_4:
      dec  ah
      mov  cl,2
      rol  ah,cl
      or   al,ah           ; number stop-bits
;
      mov  ah, byte ptr es:[bx + asc_parity]
      and  ah, 0fh         ; select partity
      cmp  ah, 1
      jne  ac_opn_6
      or   al,00001000b
      jmp  ac_opn_7
ac_opn_6:
      cmp  ah,2
      jne  ac_opn_7
      or   al,00011000b
ac_opn_7:
      add  dx,3            ;указываем на регистр контроля линии
      out  dx,al           ;посылаем инициализационное значение
;
;---инициализируем регистр разрешения прерывания
;
      mov  al, byte ptr es:[bx + asc_parity]
      and  al, 0f0h            ; clear right bits
      push dx                  ; save dx
      cmp  al, 10h             ; is it COM1 ?
      je   SetCom1             ; yes... goto SetCom1
      mov  bx, 0bh * 4
      mov  di, offset save0b
      jmp  SetCom
SetCom1:
      mov  bx, 0ch * 4
      mov  di, offset save0c
;
;  Set int for COM#
;
SetCom:
      mov  ax,0
      cli
      push es
      push ds
      push ds
      pop  es
      mov  ds, ax
      mov  si, bx
      mov  cx, 4
      rep movsb
      pop  ds
      pop  es
;
      push es
      mov  es, ax
      mov  dx,offset io_int    ;ds:dx указывают на процедуру
      mov  ax,seg io_int       ;int async. processing
      mov  es:[bx], dx
      mov  es:[bx + 2], ax
      pop  es
      sti
      pop  dx
;
ac_opn_retry:
      dec  dx              ;указываем на регистр разрешения
      dec  dx              ;прерывания
      in   al, dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      cmp  al, 0
      je   ac_opn_8
      inc  dx
      in   al, dx
      inc  dx
      inc  dx
      inc  dx               ; line status
      in   al, dx
      inc  dx               ; modem status
      in   al, dx
      mov  dx, base_addr
      in   al, dx
      inc  dx
      mov  al, 0
      out  dx, al
      inc  dx
      inc  dx
      jmp  ac_opn_retry
ac_opn_8:
      mov  err_cnt, 0
      cli                  ; Disable interrupts
      in   al,21h          ; port 21h, 8259-1 int IMR
      and  al,0E7h
      cmp  port_num, 1     ; COM1 ?
      jne  ac_opn_9        ; no it's COM2
      or   al, 00001000b
      jmp  short ac_opn_10
ac_opn_9:
      or   al, 00010000b
ac_opn_10:
      out  21h,al          ; port 21h, 8259-1 int comands
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      in   al, 21h
      cmp  port_num, 1     ; is't COM1 ?
      jne  ac_opn_100      ; no it's COM2...
      test al, 00010000b   ; it's COM1
      jz   ac_opn_101
      and  al, 11101111b
      out  21h, al
      jmp  ac_opn_101
ac_opn_100:
      test al, 00001000b
      jz   ac_opn_101
      and  al, 11110111b
      out  21h, al
ac_opn_101:
      mov  al,1            ;pазpешаем прерывания
      out  dx,al           ;посылаем байт
      inc  dx
      inc  dx
      inc  dx
ac_mdm_act:
      mov  al, 0bh         ; activate modem
      out  dx, al
      inc  dx
      inc  dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      in   al, dx
      dec  dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      in   al, dx
      test al, 0eh
      jz   ac_exit
      test al, 00000001b
      jz   ac_opn_11
      mov  dx, base_addr
      in   al, dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      inc  dx
      in   al, dx
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      mov  al, 0
      out  dx, al
      jmp  short $+2       ; delay for I/O
      jmp  short $+2       ; delay for I/O
      mov  al, 1
      out  dx, al
      add  dx, 3
      jmp  ac_mdm_act
ac_opn_11:
      dec  dx
      inc  err_cnt
      cmp  err_cnt, 10
      ja   ac_exit
      jmp  ac_mdm_act
ac_exit:
      sti                  ; Enable interrupts
      mov  err_cnt, 0
;
      mov  ax, 0           ; ret. code
ac_opn_exit:
      ret

ac_open ENDP
;

;*****************************************************
;*                                                   *
;*               C L O S E  function                 *
;*                                                   *
;*   Input  : in AH - function code.                 *
;*   Output : Return code. ( 0 - OK; -1 - error)     *
;*****************************************************
ac_close PROC
;*
;* Close subroutine
;*
;* In al port number (1 or 2)
;*
      cmp  al, port_num
      je   ac_cls_01
      jmp  ac_cls_exit
ac_cls_01:
      cmp  al, 1
      jne  ac_cls_02
      mov  di, 0bh * 4
      mov  si, offset save0b
      jmp  short ac_cls_03
ac_cls_02:
      mov  di, 0ch * 4
      mov  si, offset save0c
ac_cls_03:
      mov  ax, 0
      push es
      mov  es, ax
      mov  cx, 4
      cli
      rep movsb
      pop  es
;---получаем базовый адрес
      mov  dx,base_addr    ;получаем базовый адрес
      cmp  dx,0
      jne  ac_cls_0
      jmp  ac_cls_exit
;
ac_cls_0:
      inc  dx
      mov  al,0            ;запpещаем прерывания
      out  dx,al           ;посылаем байт
ac_cls_exit:
      sti
      ret
ac_close ENDP
;

;*****************************************************
;*                                                   *
;*               S E N D  function                   *
;*                                                   *
;*   Input  : in AH - function code.                 *
;*            in es:dx pointer to block;             *
;*            in cx - length of block                *
;*                                                   *
;*   Output : Return code. ( 0 - OK; -1 - error)     *
;*****************************************************
ac_send PROC
       and  cx, cx                ; is length valid ?
       jnz  SndProc
       cmp  cx, 2048
       jbe  SndProc
       jmp  SndExit

SndProc:
       mov  lock_send, 0          ; send no locking
       mov  bx, offset out_buff
       mov  word ptr [bx], dx     ; save offset and
       mov  word ptr [bx + 2], es ; segment of out buffer
       mov  out_full, cx          ; save out counter
       mov  out_cnt, 0            ; initial value
       mov  si, dx
       sti               ; enable int.'s
       mov  al, 65h      ; send StartBlock indicator
       call SendAL
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O

       mov  al, cl       ; send low byte of length
       call SendAl
       mov  al, ch       ; send high byte of length
       call SendAl
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O
       jmp  short $+2    ; delay for I/O

send_cycl:
       cmp  lock_send, 0 ; enable send ?
       jne  send_cycl    ; that must be replaced !!!
       mov  ah, byte ptr es:[si]
       add  dx, 5
snd_retry:
       in   al,dx
       test al,00100000b ; ready to send ?
       jz   snd_retry
       sub  dx, 5
       mov  al, ah
       out  dx, al
       dec  out_full
       inc  out_cnt
       inc  si           ; next byte
;
       loop send_cycl
;
SndExit:
       ret
ac_send ENDP

;***************************************************
;*          Send Byte from AL                      *
;*                                                 *
;*  Function : Send on async. port one byte.       *
;*  Input    : In AL byte for send.                *
;*                                                 *
;***************************************************
SendAL PROC
       push ax
       mov  dx, base_addr
send_cycXX:
       cmp  lock_send, 0 ; enable send ?
       jne  send_cycXX   ; that must be replaced !!!
       add  dx, 5
snd_retryX:
       in   al,dx
       test al,00100000b ; ready to send ?
       jz   snd_retryX
       sub  dx, 5
       pop  ax
       out  dx, al
       ret
SendAL ENDP

;*****************************************************
;*                                                   *
;*              Int 6E handler                       *
;*                                                   *
;*   Input  : in AH - function code.                 *
;*            (and for send function )               *
;*            in ds:dx pointer to block;             *
;*            in cx - length of block                *
;*                                                   *
;*   Output : Return code. ( 0 - OK; -1 - error)     *
;*****************************************************

int6e PROC  far
      push bx
      push cx
      push dx
      push si
      push di
      push ds
      push es
      push bp

      mov  bp, sp
;
      jmp  int6e_start
      dw 0
int6e_start:
      push ds
      pop  es  ; change ds and es
      push ax
      mov  bx, 0ah
      mov  ax, word ptr cs:[bx]
      mov  ds, ax       ; restory ds
      pop  ax
      cmp  ah,0         ; "open" function ?
      jne  int6e_c0
      mov  open_type, al ; Open for "PORT"
      cmp  al, 50h      ; open for PORT ?
      je   Popen
      mov  ax, 0ffffh   ; bad return code;
      jmp  int6e_exit
Popen:
      call ac_open
      jmp  int6e_exit
int6e_c0:
      cmp  ah,1         ; "close" function ?
      jne  int6e_c1
      call ac_close
      jmp  int6e_exit
int6e_c1:
      cmp  ah,2         ; "send" function ?
      jne  int6e_c2
      call ac_send
      jmp  int6e_exit
int6e_c2:
      cmp  ah,3         ; "test" function ?
      jne  int6e_c3
      mov  al,55h
int6e_c3:
      cmp  ah,4         ; "Get stat" function ?
      jne  int6e_c5
      cmp  al, 0        ; Send status ?
      jne  int6e_c4
      mov  ax, out_full ; Rest bytes for send
      jmp  int6e_exit
int6e_c4:
      cmp  al, 2        ; Send xoff ?
      jne  int6e_exit
      mov  out_full, 0
      mov  al, 19h
ctl_send:
      mov  dx, base_addr
      add  dx, 5        ; line status
when_ready5:
      in   al, dx
      test al, 00100000b ; ready to send ?
      jz   when_ready5   ; retry
      sub  dx, 5
      out  dx, al
      jmp  int6e_exit
int6e_c5:
      cmp  al, 3        ; Send xon ?
      jne  int6e_exit
      mov  out_full, 0
      mov  al, 17h      ; xon
      jmp  ctl_send
;
int6e_exit:
      pop  bp
      pop  es
      pop  ds
      pop  di
      pop  si
      pop  dx
      pop  cx
      pop  bx
      iret
int6e ENDP


;*****************************************************
;*                                                   *
;*           Handler for I/O interrupt               *
;*                                                   *
;*****************************************************

io_int PROC   far
      push ax
      push bx
      push cx
      push dx
      push si
      push di
      push ds
      push es
;
      jmp  short $ + 4
      dw   0
      mov  bx, 0ah
      mov  ax, word ptr cs:[bx]
      mov  ds, ax      ; restory DS
;
Next_int:
      mov  dx, base_addr       ;базовый адрес
      add  dx, 2               ; int. identification
      in   al, dx
      add  dx, 3               ; line status
      in   al, dx
      test al, 00001110b       ; errors ?
      jz   TestNext
      jmp  io_int_exit
TestNext:
      test al, 00000001b       ; was byte received ?
      jnz  receive
      jmp  io_int_exit
;
;
;    Receive subroutine
;
;
receive:
      mov  si, offset inbuff
      xor  bx, bx
      sub  dx, 4
      xor  ax, ax
      out  dx, al         ; disable Com# interrupt
      dec  dx
      in   al, dx
      cmp  StartBlock, 65h
      je   SaveLt
      mov  StartBlock, al
Z_exit:
      inc  dx
      mov  al, 1          ; enable RECEIVE interrupt
      out  dx, al
      jmp  io_int_exit
SaveLt:
      mov  byte ptr BlockLT, al ; save low byte of length
      add  dx,5           ;указываем на регистр статуса линии
RcvTest0:
      in   al,dx          ;получаем байт статуса
      test al,00000001b   ;проверяем получены ли данные
      jz   RcvTest0
      sub  dx, 5
      in   al,dx          ;читаем полученный символ
      mov  byte ptr BlockLT + 1, al ; save high byte of length

      mov  cx, BlockLT
      and  cx, cx
      jnz  RcvCycl
      mov  StartBlock, 0
      jmp  Z_exit

RcvCycl:
      add  dx,5           ;указываем на регистр статуса линии
RcvTest:
      in   al,dx          ;получаем байт статуса

;     test al,00011110b   ;проверяем на ошибку
;     jnz  error_routine  ;если да, то на обработку ошибки

      test al,00000001b   ;проверяем получены ли данные
      jz   RcvTest
      sub  dx, 5
      in   al,dx          ;читаем полученный символ
      mov  byte ptr [si + bx], al
      inc  bx

      loop RcvCycl
      mov  StartBlock, 0
;*
;* Block ready. Call EXIT routine
;*
      mov  cx, bx
      mov  dx, si
CallC:
;
      push dx   ; call
      push ds   ;   with "C"
      push cx   ;      convensions
      mov  ax,  2
      pushf             ; Call "_interrupt" function
      call rcv_exit
      add  sp, 6
;
      mov  b_size, 0    ; reset size of buffer

      mov  dx, base_addr
      inc  dx
      mov  al, 1
      out  dx, al       ; enable RECEIVE interrupt

io_int_exit:
      mov  dx,base_addr  ;базовый адрес
      inc  dx            ;указываем на регистр идентификации
      inc  dx            ;прерывания
      in   al,dx         ;читаем его значение
      test al,1          ;проверяем бит 1 (interrupt pending)
      jnz  Int_exit
      jmp  Next_int      ;если он установлен, то на начало

Int_exit:
      mov  al, 20h
      out  20h, al
;
      pop  es
      pop  ds
      pop  di
      pop  si
      pop  dx
      pop  cx
      pop  bx
      pop  ax
      iret
io_int ENDP
;
      END ascom
