;
; UART.ASM
; модуль управления модемом и COM-портом нижнего уровня
;

;  определяем размеры буфера приемника и ппередатчика

R_SIZE   EQU   2048     ; размер приемного буфера
S_SIZE   EQU   500      ; размер буфера передатчика


; номера обработчиков прерываний

INT_COM1 EQU   0Ch      ; COM1
INT_COM2 EQU   0Bh      ; COM2
INT_COM3 EQU   0Ch      ; COM3
INT_COM4 EQU   0Bh      ; COM4


; порты контроллера прерываний 8259

OCR   EQU   20H      ; управляющий регистр 8259
IMR   EQU   21H      ; регистр маски прерываний 8259


; константы для управления контроллером прерываний

E_IRQ4   EQU   00010000B
D_IRQ4   EQU   11101111B
EOI4     EQU   01100100B

E_IRQ3   EQU   00001000B
D_IRQ3   EQU   11110111B
EOI3     EQU   01100011B

;
; область переменных BIOS
;

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

BIOS_VAR  SEGMENT AT 40H

	rs232_base DW  4 DUP(?)

BIOS_VAR  ENDS


;
; таблица для каждого COM-порта
;

SP_TAB		STRUC

	port     DB ?  ; 1, 2, 3 или 4


; параметры для этого уровня прерываний

	int_com  DB ?  ; номер прерывания
	e_irq    DB ?
	d_irq    DB ?
	eoi      DB ?


; обработчики прерываний для этого уровня

	int_hndlr   DW ?  ; смещение обработчика прерываний
	old_com_off DW ?  ; смещение старого обработчика прерываний
	old_com_seg DW ?  ; сегмент старого обработчика прерываний

; параметры COM-порта

	installed      DB ?  ; установлен ли порт на этом компьютере? (1=да,0=нет)
	baud_rate      DW ?
	device_conn    DB ?  ; M(Модем), D(Нуль-модем)
	parity         DB ?  ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
	stop_bits      DB ?  ; 1, 2

; счетчики ошибок

	error_block DW 8 DUP(?)

; порты 8250

	DATREG   DW ?  ; регистр данных
	IER      DW ?  ; регистр управления прерывааниями
	IIR      DW ?  ; регистр идентификации прерывания
	LCR      DW ?  ; регистр управления линией
	MCR      DW ?  ; регистр управления модемом
	LSR      DW ?  ; регистр состояния линии
	MSR      DW ?  ; регистр состояния модема

	DLL      EQU   DATREG ; младший регистр делителя
	DLH      EQU   IER    ; старший регистр делителя

; указатели буферов FIFO

	; индекс первого символа в буфере передатчика
	start_s_data DW ?

	; индекс первого свободного элемента буфера передатчика
	end_s_data   DW ?

	; индекс первого символа в буфере приемника
	start_r_data DW ?

	; индекс первого свободного элемента буфера приемника
	end_r_data   DW ?


; счетчики количества символов в буферах

	size_s_data  DW ?  ; число символов в буфере передатчика
	size_r_data  DW ?  ; число символов в буфере приемника


; буфера

	send_buf       DB    S_SIZE DUP(?)  ; буфер передатчика
	reciave_buf    DB    R_SIZE DUP(?)  ; буфер приемника

SP_TAB      ENDS


EFRAME   EQU   error_block+6  ; ошибка синхронизации
EPARITY  EQU   error_block+8  ; ошибка четности
EOVFLOW  EQU   error_block    ; произошло переполнение буфера
EDSR     EQU   error_block+12 ; моодем не ответил сигналом DSR

EOVRUN   EQU   error_block+2  ; ошибка переполнения
EBREAK   EQU   error_block+4  ; обнаружен запрос на прерывание

EXMIT    EQU   error_block+10 ; ошибка при передаче
ECTS     EQU   error_block+14 ; моодем не ответил сигналом CTS



;
;
DGROUP	GROUP _DATA

_DATA SEGMENT public 'DATA'


	DIV50    DW    2304

; текущий номер области данных порта

	CURRENT_AREA   DW AREA1

; область данных для каждого порта

	AREA1 SP_TAB   <1,INT_COM1,E_IRQ4,D_IRQ4,EOI4>     ; область данных COM1
	AREA2 SP_TAB   <2,INT_COM2,E_IRQ3,D_IRQ3,EOI3>     ; область данных COM2
	AREA3 SP_TAB   <3,INT_COM3,E_IRQ4,D_IRQ4,EOI4>     ; область данных COM3
	AREA4 SP_TAB   <4,INT_COM4,E_IRQ3,D_IRQ3,EOI3>     ; область данных COM4

_DATA  ENDS



COM_TEXT    SEGMENT PARA public 'CODE'

	ASSUME   cs:COM_TEXT,ds:DGROUP,es:NOTHING

	public   _select_port
	public   _save_com
	public   _install_com
	public   _restore_com
	public   _open_com
	public   _close_com
	public   _dtr_on
	public   _dtr_off
	public   _r_count
	public   _s_count
	public   _receive_com
	public   _send_com
	public   _break_com
	public   _com_errors



;
; выбор активного порта
;  [bp+6] - номер порта

_select_port   PROC FAR

	push  bp
	mov   bp, sp
	mov   ax, [bp+6]  ;получаем в ax аргумент функции

	cmp   al,1     ; установлен порт 1?
	je    port1    ; да

	cmp   al,2     ; установлен порт 2?
	je    port2    ; да

	cmp   al,3     ; установлен порт 3?
	je    port3    ; да

	cmp   al,4     ; установлен порт 4?
	je    port4    ; да

	jmp set_carrent_area


port1:
	mov   ax,OFFSET DGROUP:AREA1  ; выбираем область данных  COM1
	jmp   short set_carrent_area

port2:
	mov   ax,OFFSET DGROUP:AREA2  ; выбираем область данных  COM2
	jmp   short set_carrent_area

port3:
	mov   ax,OFFSET DGROUP:AREA3  ; выбираем область данных  COM3
	jmp   short set_carrent_area

port4:
	mov   ax,OFFSET DGROUP:AREA4  ; выбираем область данных  COM4

set_carrent_area:

	; записываем в переменной CURRENT_AREA смещение
	; текущей области данных

	mov   CURRENT_AREA,ax

	mov sp,bp
	pop bp
	ret

_select_port   ENDP


;
; сохранение текущего вектора COM прерывания
;
_save_com	PROC FAR

	push bp
	mov bp,sp
	push si

	; записываем в si указатель на текущую область данных

	mov   si,CURRENT_AREA
	push  es

	mov   AREA1.int_hndlr,OFFSET int_hndlr1
	mov   AREA2.int_hndlr,OFFSET int_hndlr2
	mov   AREA3.int_hndlr,OFFSET int_hndlr3
	mov   AREA4.int_hndlr,OFFSET int_hndlr4

; сохраняем старый вектор прерывания

	mov   ah,35H
	mov   al,int_com[si] ; номер прерывания
	int   21h

; записываем в переменные old_com_off и old_com_seg
; соответственно сегмент и смещение старого вектора прерывания

	mov   old_com_off[si],bx
	mov   bx,es
	mov   old_com_seg[si],bx

	pop   es

	pop si
	mov sp,bp
	pop bp

	ret

_save_com	ENDP


;
; install_com: установить активный порт
;
; возвращает в регистре ax - 1 при успешной установке
; и 0 в случае ошибки
;
_install_com   PROC FAR

	push bp
	mov bp,sp
	push si

	mov   si,CURRENT_AREA

	push  es

	cmp   installed[si],1
	jne   go_install
	jmp   alredy_ok

; очищаем счетчики ошибок

go_install:

	mov   WORD PTR EOVFLOW[si],0  ; переполнение буфера передатчика
	mov   WORD PTR EOVRUN[si],0   ; ошибка переполнения при приеме
	mov   WORD PTR EBREAK[si],0   ; обнаружен запрос на прерывание
	mov   WORD PTR EFRAME[si],0   ; ошибка синхронизации
	mov   WORD PTR EPARITY[si],0  ; ошибка четности
	mov   WORD PTR EXMIT[si],0    ; ошибка при передаче
	mov   WORD PTR EDSR[si],0     ; не получен сигнал DSR
	mov   WORD PTR ECTS[si],0     ; не получен сигнал CTS


; определяем базовый адрес используемого COM порта

	mov   bx,BIOS_VAR
	mov   es,bx

	ASSUME   es:BIOS_VAR

	cmp   port[si],1  ; порт 1?
	je    adr_3F8

	cmp   port[si],2  ; порт 2?
	je    adr_2F8

	cmp   port[si],3  ; порт 3?
	je    adr_3E8

	cmp   port[si],4  ; порт 4?
	je    adr_2E8

	int   20H

adr_3F8:
	mov   ax,3F8H
	jmp   cmp_bios


adr_2F8:
	mov ax,2F8H
	jmp   cmp_bios

adr_3E8:
	cmp   rs232_base+4,0
	je    adr_3E8_A

	mov   ax,rs232_base+4
	jmp   cmp_bios

adr_3E8_A:
	mov   ax,3E8H
	mov   rs232_base+4,ax
	jmp   cmp_bios


adr_2E8:
	cmp   rs232_base+6,0
	je    adr_2E8_A

	mov   ax,rs232_base+6
	jmp   cmp_bios

adr_2E8_A:
	mov   ax,2E8H
	mov rs232_base+6,ax

; проверяем существует определена ли соответствующаая переменная
; BIOS

cmp_bios:
	cmp   ax,rs232_base
	je    set_reg_adr

	cmp   ax,rs232_base+2
	je    set_reg_adr

	cmp   ax,rs232_base+4
	je    set_reg_adr

	cmp   ax,rs232_base+6
	jne   bad_exit


set_reg_adr:

	mov   bx,DATREG
	mov   cx,7

set_next_reg_adr:

	mov   WORD PTR [si][bx],ax
	inc   ax
	add   bx,2
	loop  set_next_reg_adr

; устанавливаем вектор прерывания на наш обработчик

	mov   AREA1.int_hndlr,OFFSET int_hndlr1
	mov   AREA2.int_hndlr,OFFSET int_hndlr2
	mov   AREA3.int_hndlr,OFFSET int_hndlr3
	mov   AREA4.int_hndlr,OFFSET int_hndlr4

	mov   ah,25H
	mov   al,int_com[si] ; номер прерывания
	mov   dx,OFFSET DGROUP:int_hndlr[si]
	push  ds
	push  cs
	pop   ds
	int   21h

	pop   ds

	; поднимаем флаг - порт установлен

alredy_ok:
	mov   installed[si],1
	pop   es

	; возвращаем 1

	mov ax,1
	pop si
	mov sp,bp
	pop bp
	ret

; порт не установлен

bad_exit:
	mov installed[si],0
	pop   es

	; возвращаем 0

	mov ax,0
	pop si
	mov sp,bp
	pop bp
	ret

_install_com   ENDP


;
; восстановление векторов прерываний
;
_restore_com   PROC FAR

	push bp
	mov bp,sp
	push si

; отмечаем COM порт как не активный

	mov   si,CURRENT_AREA
	mov   installed[si],0

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

	mov   ah,25H
	mov   al,int_com[si]
	mov   dx,old_com_off[si]
	mov   bx,old_com_seg[si]
	push  ds
	mov   ds,bx
	int   21h

	pop   ds

	pop   si
	mov   sp,bp
	pop   bp
	ret

_restore_com   ENDP


;
; открыть COM порт
;
; сброс буферов передатчика и приемника,
; инициализация регистров UART 8250
; разрешение прерываний от UART 8250
; (программирование контроллера прерываний)
;
; [bp+6] = скорость обмена
; [bp+8] = способ соединения -  M(Модем), D(Нуль-модем)
; [bp+10] = четность - N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
; [bp+12] = число стоповых битов 1, 2
;
_open_com   PROC FAR

	push bp
	mov bp,sp
	push si

	mov   si,CURRENT_AREA

 ; запрещаем прерывания

	cli
	mov   ax,[bp+6]
	mov   baud_rate[si],ax
	mov   bh,[bp+8]
	mov   device_conn[si],bh
	mov   bl,[bp+10]
	mov   parity[si],bl
	mov   ch,[bp+12]
	mov   stop_bits[si],CH


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

	mov   start_s_data[si],0
	mov   end_s_data[si],0
	mov   start_r_data[si],0
	mov   end_r_data[si],0
	mov   size_s_data[si],0
	mov   size_r_data[si],0

	; проверяем установлен ли уже обработчик прерываний

	test  installed[si],1
	jnz   reset_uart
	jmp   exit_open

reset_uart:

; устанавливаем регистры UART 8250

; сбрасываем регистр управления модемом

	mov   al,0
	mov   dx,MCR[si]
	out   dx,al
	jmp   $+2

	; сбрасываем регистр состояния линии

	mov   dx,LSR[si]
	in    al,dx
	jmp   $+2

	; сбрасываем регистр данных

	mov   dx,DATREG[si]
	in    al,dx
	jmp   $+2

	; сбрасываем регистр состояния модема

	mov   dx,MSR[si]
	in    al,dx

	; определяем делитель частоты тактового генератора

	mov   ax,50
	mul   DIV50
	div   baud_rate[si]
	mov   bx,ax

	; переключаем регисир данных и регистр управления прерываниями
	; для ввода делителя частоты тактового генератора

	mov   dx,LCR[si]
	mov   al,80H
	out   dx,al
	jmp   $+2

	; вводим младший байт делителя частоты тактового генератора

	mov   dx,WORD PTR DLL[si]
	mov   al,bl
	out   dx,al
	jmp   $+2

	; вводим старший байт делителя частоты тактового генератора

	mov   dx,WORD PTR DLH[si]
	mov   al,bh
	out   dx,al
	jmp   $+2

; определяем четность и число стоповых битов

	mov   al,03H
	cmp   parity[si],'O'
	jne   next1
	mov   al,0ah
	jmp   short next3

next1:
	cmp   parity[si],'E'
	jne   next2
	mov   al,1ah
	jmp   short next3

next2:
	cmp   parity[si],'M'
	jne   next3
	mov   al,2ah

next3:
	test  stop_bits[si],2
	jz stop1
	or al,4

stop1:
	mov   dx,LCR[si]
	out   dx,al

; разрешаем прерывания для 8259 и 8250

	; устанавливаем регистр маски прерываний чтобы
	; разрешить прерывания от асинхронного порта

	in    al,IMR
	and   al,d_irq[si]
	out   IMR,al

	; разрешаем генерацию прерываний при готовности принимаемых
	; данных, по состоянию "BREAK" и по ошибке

	mov   dx,IER[si]
	mov   al,5
	out   dx,al
	jmp   $+2

	; устанавливаем DTR, RTS, OUT2

	mov   dx,MCR[si]
	mov   al,0bh
	out   dx,al

exit_open:

	sti

	pop si
	mov sp,bp
	pop bp
	ret

_open_com	ENDP


;
; запрещаем прерывания от асинхронного порта
;
_close_com	PROC FAR
	push bp
	mov bp,sp
	push si

	mov   si,CURRENT_AREA
	test  installed[si],1
	jz    exit_close

; запрещаем прерывания UART 8250

	mov   dx,IER[si]
	mov   al,0
	out   dx,al

; маскируем прерывания от UART

	mov   dx,IMR
	in    al,dx
	or    al,e_irq[si]
	jmp   $+2
	out   dx,al

exit_close:

	pop si
	mov sp,bp
	pop bp
	ret

_close_com	ENDP


;
; снимаем сигнал DTR
;

_dtr_off	PROC FAR

	push bp
	mov bp,sp
	push si

	pushf
	push  ax
	push  dx
	push  si

	mov   si,CURRENT_AREA
	test  installed[si],1
	jz    exit_dtr_off

	; устанавливаем регистр управления модемом,
	; сбрасываем сигналы DTR и RTS

	mov   dx,MCR[si]
	mov   al,08H
	out   dx,al

exit_dtr_off:

	pop   si
	pop   dx
	pop   ax
	popf

	pop si
	mov sp,bp
	pop bp

	ret

_dtr_off	ENDP


;
; устанавливаем сигнал DTR
;

_dtr_on PROC FAR

	push bp
	mov bp,sp
	push si

	pushf
	push  ax
	push  dx
	push  si
	mov   si,CURRENT_AREA
	test  installed[si],1
	jz    exit_dtr_on

	; устанавливаем регистр управления модемом,
	; устанавливаем сигналы DTR, RTS, OUT2

	mov   dx,MCR[si]
	mov   al,0bh
	out   dx,al

exit_dtr_on:

	pop   si
	pop   dx
	pop   ax
	popf

	pop si
	mov sp,bp
	pop bp

	ret

_dtr_on ENDP


;
; возвращаем в регистре ax число байтов в регистре приемника,
; а в регистре dx общий размер буфера приемника
;

_r_count	PROC FAR
	push bp
	mov bp,sp
	push si

	pushf
	push  si
	mov   si,CURRENT_AREA

	mov   ax,0
	mov   dx,R_SIZE

	test  installed[si],1
	jz    exit_r_count

	; записываем в регистр ax число символов в буфере приемника

	mov   ax,size_r_data[si]

exit_r_count:

	pop   si
	popf

	pop si
	mov sp,bp
	pop bp
	ret

_r_count	ENDP


;
; получаем очередной символ из буфера приемника,
; полученный символ удаляется из буфера
;

_receive_com	PROC FAR

	push bp
	mov bp,sp
	push si

	pushf
	push  bx
	push  si

	mov   si,CURRENT_AREA
	mov   ax,-1

	test  installed[si],1
	jz    exit_receive_com

	; возвращаемся если буфер приемника пуст

	cmp   size_r_data[si],0
	je    exit_receive_com


	mov   ah,0
	mov   bx,start_r_data[si]
	mov   al,reciave_buf[si][bx]


	cmp   parity[si],'N'
	je no_parity

	; если производится поверка на четность, то маскируем старший бит

	and   al,7FH

no_parity:

	inc   bx
	cmp   bx,R_SIZE
	jb    rec_ptr_no_max
	mov   bx,0

rec_ptr_no_max:

	mov   start_r_data[si],bx
	dec   size_r_data[si]

exit_receive_com:

	pop   si
	pop   bx
	popf

	pop si
	mov sp,bp
	pop bp

	ret

_receive_com	ENDP



;
; функция возвращает в регистре ax число свободных байт в
; буфере передатчика, а в регистре dx общий размер буфера передатчика
;
_s_count	PROC FAR

	push bp
	mov bp,sp
	push si

	pushf
	push  si

	mov   si,CURRENT_AREA
	mov   ax,0
	mov   dx,S_SIZE

	test  installed[si],1
	jz    exit_s_count

	mov   ax,S_SIZE
	sub   ax,size_s_data[si]

exit_s_count:

	pop   si
	popf

	pop si
	mov sp,bp
	pop bp

	ret

_s_count	ENDP



;
; поместить символ в буфер передатчика
;  [bp+6] - символ
;
_send_com	PROC FAR

	push bp
	mov bp,sp
	push si

	mov al,[bp+6]

	pushf
	push  ax
	push  bx
	push  dx
	push  si

	mov   si,CURRENT_AREA

	test  installed[si],1
	jz    exit_send_com

	cmp   size_s_data[si],S_SIZE
	jl    no_s_EOVFLOW

	; произошло переполнение буфера передатчика

	inc   WORD PTR EOVFLOW[si]
	jmp   short exit_send_com

no_s_EOVFLOW:

	mov   bx,end_s_data[si]
	mov   send_buf[si][bx],al
	inc   bx
	cmp   bx,S_SIZE
	jl    no_send_ptr_max
	mov   bx,0

no_send_ptr_max:

	mov   end_s_data[si],bx
	inc   size_s_data[si]

	; считываем регистр управления прерываниями

	mov   dx,IER[si]
	in    al,dx

	; завершаем функцию если разрешены прерывания после передачи байта

	test  al,2
	jnz   exit_send_com

	; разрешаем прерывания после передачи байта, после прииема байта,
	; при обнаружении состояния "BREAK" и при возникновении ошибки

	mov   al,7
	out   dx,al

exit_send_com:

	pop   si
	pop   dx
	pop   bx
	pop   ax
	popf

	pop si
	mov sp,bp
	pop bp

	ret

_send_com	ENDP


;
; S_local
;
_send_local PROC FAR
	push bp
	mov bp,sp
	push si

	mov al,[bp+6]
	pushf
	push  ax
	push  bx
	push  si
	mov   si,CURRENT_AREA
	test  installed[si],1
	jz    SLX

	cli
	cmp   size_r_data[si],R_SIZE
	jb    L13A
	inc   WORD PTR EOVFLOW[si]
	jmp   short L14

L13A:
	mov   bx,end_r_data[si]
	mov   reciave_buf[si][bx],al
	inc   bx
	cmp   bx,R_SIZE
	jl    L13
	mov   bx,0

L13:
	mov   end_r_data[si],bx
	inc   size_r_data[si]

L14:
	sti

SLX:
	pop   si
	pop   bx
	pop   ax
	popf
	pop si
	mov sp,bp
	pop bp
	ret
_send_local ENDP



;
; передаем удаленному модему сигнал "BREAK"
;

_break_com	PROC FAR

	push bp
	mov bp,sp
	push si

	pushf
	push  ax
	push  cx
	push  dx

	mov   si,CURRENT_AREA
	test  installed[si],1
	jz    exit_break_com

	; передаем сигнал "BREAK"

	mov   dx,LCR[si]
	in    al,dx
	jmp   $+2
	or    al,40h
	out   dx,al

	mov   cx,0C000h

do_BREAK:
	loop   do_BREAK

	and   al,0BFh
	out   dx,al

exit_break_com:

	pop   dx
	pop   cx
	pop   ax
	popf

	pop si
	mov sp,bp
	pop bp

	ret

_break_com	ENDP



;
; возвращаем в dx:ax указатель на счетчики ошибок
;
_com_errors PROC FAR

	push  bp

	mov   bp,sp
	mov   ax,OFFSET DGROUP:CURRENT_AREA
	add   ax,error_block
	mov   dx,ds
	mov   sp,bp

	pop   bp

	ret
_com_errors ENDP


;
; заполняем счетчики ошибок
;

set_err   PROC  NEAR

	test  al,2
	jz    test1
	inc   WORD PTR EOVRUN[si]

test1:
	test  al,4
	jz    test2
	inc   WORD PTR EPARITY[si]

test2:
	test  al,8
	jz    test3
	inc   WORD PTR EFRAME[si]

test3:
	test  al,16
	jz    exit_set_err
	inc   WORD PTR EBREAK[si]

exit_set_err:

	ret

set_err   ENDP


;
; протокол модема для передачи данных
;

modem_protocol PROC NEAR

	cmp   device_conn[si],'M'
	jne   no_modem

; устанавливаем сигналы DTR, RTS и OUT2

	mov   dx,MCR[si]
	mov   al,00001011B
	out   dx,al
	jmp   $+2

; ожидаем пока модем ответит о готовности сигналом DSR

	mov   cx,1000
	mov   dx,MSR[si]

wait_dsr:
	in    al,dx
	test  al,20H
	jnz   test_cts
	loop  wait_dsr

	; модем не ответил сигналом DSR

	inc   WORD PTR EDSR[si]
	jmp   short no_modem

test_cts:

; ожидаем пока модем ответит о готовности сигналом CTS

	mov   cx,1000

wait_cts:
	in    al,dx
	test  al,10H
	jnz   test_lcr
	loop  wait_cts

	; модем не ответил сигналом CTS

	inc   WORD PTR ECTS[si]

test_lcr:
no_modem:

	; проверяем пуст ли регситр хранения передатчика

	mov   dx,LSR[si]
	in    al,dx
	test  al,20H
	jnz   s_reg_empty

	; ошибка при передаче

	inc   WORD PTR EXMIT[si]

s_reg_empty:

	ret

modem_protocol ENDP


;
; обработчик прерываний от COM1
;

int_hndlr1 PROC  FAR

	push  si
	mov   si,OFFSET DGROUP:AREA1
	jmp   short handle_int

;
; обработчик прерываний от COM2
;

int_hndlr2 PROC  FAR

	push  si
	mov   si,OFFSET DGROUP:AREA2
	jmp   short handle_int

;
; обработчик прерываний от COM3
;

int_hndlr3 PROC  FAR

	push  si    ; SAVE si
	mov   si,OFFSET DGROUP:AREA3
	jmp   short handle_int

;
; обработчик прерываний от COM4
;

int_hndlr4 PROC  FAR

	push  si    ; SAVE si
	mov   si,OFFSET DGROUP:AREA4


;
; обработчик прерываний
;

handle_int:

	push  ax
	push  bx
	push  cx
	push  dx
	push  bp
	push  di
	push  ds
	push  es

	mov   ax,DGROUP
	mov   ds,ax

next_pr:

; передаем контроллеру прерываний команду конца обработки
; прерывания

	mov   dx,OCR
	mov   al,eoi[si]
	out   dx,al

next_inter:

	; считываем значение регистра идентификации прерывания

	mov   dx,IIR[si]
	in    al,dx

	; определяем причину прерывания

	; данные приняты и доступны для чтения
	cmp   al,4
	je    RX_int

	; буфер передатчика пуст
	cmp   al,2
	je    TX_int

	; изменилось состояние линий CTS, RI, DCD, DSR
	cmp   al,6
	je    LSTAT_int

	; обнаружено сотояние "BREAK" или произошла ошибка
	cmp   al,0
	je    MSTAT_int

	; завершаем обработку прерываний

	jmp   FAR PTR exit_handler

LSTAT_int:

	; считываем регистр сотояния линии и вызываем функцию
	; set_err, которая определит причину прерывания

	mov   dx,LSR[si]
	in    al,dx
	call  set_err
	jmp   next_inter

MSTAT_int:

	; считываем регистр состояния модема

	mov   dx,MSR[si]
	in    al,dx
	jmp   next_inter

TX_int:

	; смотрим есть ли данные для передачи модему

	cmp   size_s_data[si],0

	jg    have_data_for_send

; если буфер передатчика пуст переустанавливаем регистр
; управления прерываниями

	mov   dx,IER[si]
	mov   al,5
	out   dx,al
	jmp   next_inter

have_data_for_send:

	; передаем символ модему в соответствии с состоянием
	; линий RS-232-С

	call  modem_protocol

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

	mov   bx,start_s_data[si]
	mov   al,send_buf[si][bx]
	mov   dx,DATREG[si]
	out   dx,al

	inc   bx
	cmp   bx,S_SIZE
	jb    ptr_no_max
	mov   bx,0

ptr_no_max:

	mov   start_s_data[si],bx
	dec   size_s_data[si]
	jmp   next_inter


; данные приняты и доступны для чтения
RX_int:

	; считываем принятый байт из регистра данных UART

	mov   dx,DATREG[si]
	in    al,dx

	cmp   size_r_data[si],R_SIZE
	jl    no_r_EOVFLOW

	; буфер приемника переполнен, увеличиваем соответствующий
	; счетчик ошибок

	inc   WORD PTR EOVFLOW[si]
	jmp   next_inter

no_r_EOVFLOW:

	mov   bx,end_r_data[si]
	mov   reciave_buf[si][bx],al
	inc   size_r_data[si]
	inc   bx

	cmp   bx,R_SIZE
	jb    no_max_r_ptr
	mov   bx,0

no_max_r_ptr:

	mov   end_r_data[si],bx

	jmp   next_inter

exit_handler:

	mov   al,20h
	out   20h,al

	pop   es
	pop   ds
	pop   di
	pop   bp
	pop   dx
	pop   cx
	pop   bx
	pop   ax

	pop   si
	iret

int_hndlr4 ENDP
int_hndlr3 ENDP
int_hndlr2 ENDP
int_hndlr1 ENDP

COM_TEXT ENDS

END
