;prg15_1.asm
MASM
MODEL	small	;модель памяти
STACK	256	;размер стека
.486p
delay	macro	time
local	ext,iter
;макрос задержки
;На входе - значение переменной задержки (в мкс)
	push	cx
	mov	cx,time
ext:
	push	cx
;в cx одна мкс[FA1],это значение можно
;поменять в зависимости от производительности процессора
	mov	cx,5000
iter:
	loop	iter
	pop	cx
	loop	ext
	pop	cx
endm	;конец макроса
.data
tonelow	dw	2651	;нижняя граница звучания 450 Гц
cnt	db	0	;счётчик для выхода из программы
temp	dw	?	;верхняя граница звучания
old_off8	dw	0	;для хранения старых значений вектора
old_seg8	dw	0	;сегмент и смещение
time_1ch	dw	0	;переменная для пересчета
.code			;начало сегмента кода
off_1ch	equ	1ch*4	;смещение вектора 1ch в ТВП
off_0ffh	equ	0ffh*4	;смещение вектора ffh в ТВП
char	db	'0'	;символ для вывода на экран
maskf	db	07h	;маска вывода символов на экран
position	dw	2000	;позиция на экране - почти центр
main	proc
	mov	ax,@data
	mov	ds,ax
	xor	ax,ax
	cli	;запрет аппаратных прерываний на время
;замены векторов  [FA2] прерываний
;замена старого вектора 1ch на адрес new_1ch
;настройка es на начало таблицы векторов
;прерываний - в реальном режиме:
	mov	ax,0
	mov	es,ax
;сохранить старый вектор
	mov	ax,es:[off_1ch]	;смещение старого вектора 1ch в ax
	mov	old_off8,ax	;сохранение смещения в old_off8
	mov	ax,es:[off_1ch+2]	;сегмент старого вектора 1ch в ax
	mov	old_seg8,ax	;сохранение сегмента в old_seg8
;записать новый вектор в таблицу векторов прерываний
	mov	ax,offset new_1ch	;смещение нового обработчика в ax
	mov	es:off_1ch,ax
	push	cs
	pop	ax	;настройка ax на cs
	mov	es:off_1ch+2,ax	;запись сегмента
;инициализировать вектор пользовательского прерывания 0ffh
	mov	ax,offset new_0ffh
	mov	es:off_0ffh,ax	;прерывание 0ffh
	push	cs
	pop	ax
	mov	es:off_0ffh+2,ax
	sti		;разрешение аппаратных прерываний
;задержка, чтобы новый обработчик таймера вывел символы на экран
	delay	3500
;завершение программу
	int	0ffh
exit:
	mov	ax,4c00h
	int	21h
main	endp
new_1ch	proc	;новый обработчик прерывания от таймера
;сохранение в стеке используемых регистров
	push	ax
	push	bx
	push	es
	push	ds
;настройка ds на cs
	push	cs
	pop	ds
;запись в es адреса начала видеопамяти - B800:0000
	mov	ax,0b800h
	mov	es,ax
	mov	al,char	;символ в al
	mov	ah,maskf	;маску вывода - в ah
	mov	bx,position	;позицию на экране - в bx
	mov	es:[bx],ax	;вывод символа в центр экрана
	add	bx,2	;увеличение позиции
	mov	position,bx	;сохранение новой позиции
	inc	char	;следующий символ
;восстановление используемых регистров:
	pop	ds
	pop	es
	pop	bx
	pop	ax
	iret		;возврат из прерывания
new_1ch	endp	;конец обработчика
new_0ffh	proc	;новый обработчик пользовательского прерывания
sirena:
;сохранение в стеке используемых регистров
	push	ax
	push	bx
;проверка для пересчета на 4:
	test	time_1ch,03h
	jnz	leave_it	;если два правых бита не 11, то на выход, иначе:
go:
	mov	ax,0B06h	;заносим слово состояния 110110110b
;(0В6h)-выбираем второй канал порта 43h (динамик)
	out	43h,ax	;в порт 43h
	in	al,61h	;получим значение порта 61h в al
	or	al,3	;инициализируем динамик - подаем ток
	out	61h,al	;в порт 61h
	mov	cx,2083	;количество шагов
musicup:
;значение нижней границы частоты в ax (1193000/2651=450 Гц),
;где 1193000 - частота динамика
	mov	ax,tonelow
	out	42h,al	;в порт 42h - младшее слово ax:al
	mov	al,ah	;обмен между al и ah
	out	42h,al	;в порте 42h уже старшее слово ax:ah
	add	tonelow,1	;увеличение частоты
	delay	1	;задержка на 1 мкс
	mov	dx,tonelow	;текущее значение частоты - в dx
	mov	temp,dx	;в temp - верхнее значение частоты
	loop	musicup	;повторить цикл повышения
	mov	cx,2083
musicdown:
	mov	ax,temp	;верхнее значение частоты - в ax
	out	42h,al	;младший байт ax:al в порт 42h
	mov	al,ah	;обмен между al и ah
	out	42h,al	;старший байт ax:ah в порт 42h
	sub	temp,1	;уменьшение частоты
	delay	1	;задержка на 1 мкс
	loop	musicdown	;повторить цикл понижения
nosound:
	in	al,61h	;значение порта 61h - в al
;слово состояния 0fch - выключение динамика и таймера
	and	al,0fch
	out	61h,al	;в порт 61h
	mov	dx,2651	;для последующих циклов
	mov	tonelow,dx
	inc	cnt	;инкремент количества проходов
	cmp	cnt,2	;если сирена не звучала двух
			;раз - повторный запуск
	jne	go
leave_it:		;выход
	inc	time_1ch	;пересчет на 4
;восстановление используемых регистров
	pop	bx
	pop	ax
;восстановление вектора прерывания от таймера
	cli		;запрет аппаратных прерываний
	xor	ax,ax	;снова настройка es на начало таблицы
	mov	es,ax	;векторов прерываний
	mov	ax,old_off8	;запись в таблицу смещения старого
	mov	es:off_1ch,ax	;обработчика прерывания от таймера
	mov	ax,old_seg8	;запись сегмента
	mov	es:off_1ch+2,ax
	sti	;разрешение аппаратных прерываний
	iret	;возврат из прерывания
new_0ffh	endp	;конец обработчика
end	main	;конец программы

[FA1]Что значит "значение одной мкс". Может быть просто: "Одна микросекунда. Значение можно поменять:"? Исправил.
[FA2]М.б. "на время перепрограммирования"? Да.
