В) Использование команды JMP dword ptr
Assume CS: Code;, DS: Code
Code SEGMENT
org 100h
Start: jmp start1
len equ dend-dal ; Длина пересылаемой процедуры
s_dal equ 8800h ; Сегмент места пересылки процедуры
addr dw 0,s_dal ; Дальний адрес места назначения
start1: cld
mov ax,cs
mov ds,ax ; DS = CS
; Перенос процедуры DAL в область 8800h
les di,dword ptr addr ; ES:DI = s_dal:0000
lea si,dal ; SI = offset DAL
mov cx,len ; CX = длине пересылаемой проц.
rep stosb ; DS:SI Þ ES:DI
; Стирание процедуры в тексте
mov ax,cs
mov es,ax ; ES = CS
lea di,dal ; DI = offset DAL
mov cx,len ; CX = длине пересылаемой проц.
xor ax,ax ; АХ=0 для стирания процедуры
rep movsb ; AX(0) Þ ES:DI
; Вычисление значения DS для дальней процедуры
push ds ; DS Þ ctack
lea ax,dal ; offset DAL
xor dx,dx ; Мы знаем, что это смещение
mov cx,16 ; нацело делится на 16
div cx ; AX = (offset DAL)/16
mov dx,ax ; DX = (offset DAL)/16
mov ax,s_dal
sub ax,dx ; AX = s_dal - (offset DAL)/16
mov ds,ax ; DS = s_dal - (offset dal)/16
;**** Переменная часть Подготовка возврата в стеке
mov ax,cs
push ax ; CS Þ stack
lea ax,l ; CS:(offset L) - точка возврата
push ax ; offset L Þ stack
; Переход на дальнюю процедуру
jmp dword ptr addr ; Переход на s_dal:0000
;*************** Конец переменной части ******************
; Точка возврата - печать сообщения о завершении
l: pop ds ; Восстановление местного DS
mov ah,9 ; Функция 9
lea dx,text ; DS:DX - смещение текста сообщения
int 21h ; Вызов функции DOS
; Ожидание ввода клавиши пробела
loop1: mov ah,0 ; Функция 0
int 16h ; Клавиатурное прерывание
cmp al,' ' ; Пробел ?
jnz loop1 ; Нет !
int 20h
text db 0dh,0ah,0ah,'Программа выполнена',0dh,0ah,'$'
; Выравнивание начала засылаемой процедуры на начало параграфа
if ($-start)mod 16 (если не 0) ; Псевдокоманда
org $+(16-($-start)mod 16) ; условного
endif ; ассемблирования
; Процедура, которая пересылается в область s_dal.
; Она начинается здесь на границе параграфа.
; Когда эта процедура находится в области s_dal,
; значение DS = s_dal - (offset dal)/16
dal proc far ; Дальняя процедура (т.е. будет дальний возврат - retf)
mov ah,9 ; Функция 9
lea dx,text1 ; DS:DX - смещение текста сообщения
int 21h ; Вызов функции DOS
; Ожидание ввода клавиши ENTER
dal1: mov ah,0 ; Функция 0
int 16h ; Клавиатурное прерывание
cmp al,0dh ; ENTER ?
jnz dal1 ; Нет !
ret ; Возврат дальний !!!
text1 db 0dh,0ah,0ah,'Я нахожусь по адресу S_DAL',0dh,0ah,'$'
dal endp
dend: ; Метка для определения конца пересылаемой процедуры
Code ENDS ; Конец сегмента (кодового)
END Start ; Указание точки входа в программу
2.2 РАБОТА С ФАЙЛАМИ
Задача 2.2.1. Используя функции прерывания 21h DOS, написать программу, которая
· создает в текущем каталоге новый файл с именем, соот ветствующим фамилии студента
· записывает в созданный файл текст длиной не менее 40 символов
· изменяет дату создания файла на 11 ноября 1991 года (при этом время создания должно остаться неизменным)
При возникновении ошибки во время выполнения какой-либо функции должно выводиться сообщение о возникновении ошибки, и программа должна завершаться. Классифицировать возникшую ошибку не надо.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
Start proc near
mov ax,cs
mov ds,ax ; DS = CS
; Создание нового файла
mov ah,5bh ; Функция создания нового файла
mov cx,2 ; Атрибут "скрытый"
lea dx,file ; Адрес спецификации файла
int 21h ; Функция DOS
jnc m1 ; Переход, если ошибки нет
; Вывод сообщения при возникновении ошибки
err1: mov ah,9 ; Функция вывода строки на экран
lea dx,meserr ; Адрес сообщения об ошибке
int 21h
int 20h
m1: mov handle,ax ; Сохранение дескриптора файла
; Запись текста в файл
mov ah,40h ; Функция записи в файл
mov bx,handle ; Дескриптор файла
mov cx,40 ; Длина записываемого текста
lea dx,text ; Адрес записываемого текста
int 21h
jc err1 ; Переход на вывод сообщения при ошибке
; Получение даты и времени создания файла
mov ah,57h ; Функция работы с датой и временем
mov al,0 ; Подфункция получения даты и времени
mov bx,handle ; Дескриптор файла
int 21h
jc err1 ; Переход на вывод сообщения при ошибке
; Изменение даты создания файла
mov al,1 ; Подфункция установки даты и времени
mov dx,0001011101101011b ; Год, месяц и день
int 21h
jc err1 ; Переход на вывод сообщения при ошибке
int 20h ; Выход из программы
file db 'roschin.ie4',0
meserr db 'Error during program execution$'
text db '*** This file was created by Roschin *** '
handle dw ? ; Место для дескриптора файла
Start endp
Code ENDS
END Start
Задача 2.2.2. Используя функции прерывания 21h DOS, написать программу, которая
· создает в текущем каталоге новый файл с именем, соответствующим фамилии студента
· записывает в созданный файл строку, вводимую с клавиатуры (только ее, и ничего лишнего) в каталог ...\PROGRAM\FILES
При возникновении ошибки во время выполнения какой-либо функции должно выводиться сообщение о возникновении ошибки, и программа должна завершаться. Классифицировать возникшую ошибку не надо.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
Start proc near
mov ax,cs
mov ds,ax ; DS = CS Далее идет ввод текста с клавиатуры
mov ah,0ah ; Функция ввода с клавиатуры
lea dx,buf ; Адрес буфера
int 21h
; Создание нового файла
mov ah,5bh ; Функция создания нового файла
mov cx,2 ; Атрибут "скрытый"
lea dx,file ; Адрес спецификации файла
int 21h ; Функция DOS
jc err1 ; Переход, если ошибки нет
mov handle,ax ; Сохранение дескриптора файла
; Запись текста в файл
mov ah,40h ; Функция записи в файл
mov bx,handle ; Дескриптор файла
xor ch,ch
mov cl,buf+1 ; Длина записываемого текста
lea dx,buf+2 ; Адрес записываемого текста
int 21h
jc err1 ; Переход на вывод сообщения при ошибке
; Закрытие файла
mov ah,3eh ; Функция закрытия файла
mov bx,handle ; Дескриптор файла
int 21h
jc err1
int 20h ; Выход из программы
; Вывод сообщения при возникновении ошибки
err1: mov ah,9 ; Функция вывода строки на экран
lea dx,meserr ; Адрес сообщения об ошибке
int 21h
int 20h
file db 'roschin.ie4',0
buf db 255,255 dup(0)
meserr db 'Error during program execution$'
handle dw ? ; Место для дескриптора файла
Start endp
Code ENDS
END Start
2.3 РАБОТА С ГРАФИКОЙ
Задача 2.3.1. Написать программу, которая переключает ЭВМ в графический режим CGA (4 цвета, 320 * 200 точек) и заполняет экранную область (8000h байтов, начиная с адреса B800h) заданным значением (color), выводит на середину экрана изображение мяча (4*4) точек и обеспечивает его движение с отражением от краев экрана.
Assume CS: Code, DS: Code
screen_size equ 8000h ; Размер экрана в байтах
color equ 0 ; Цвет: 4 точки в байте, 55р - голубой,
Code SEGMENT ;0ааh - сиреневый, 0ffh - белый
org 100h
Start proc near
mov ah,0 ; Функция установки видеорежима
mov al,4 ; Графический режим 4 цвета 320 * 200
int 10h
call lab1
call lab2
int 20h
Start endp
lab2 proc near
mov dl,40 ; Координата Х (0 - 79)
mov dh,50 ; Координата Y (0 - 99)
mov cl,1 ; Дельта X
mov ch,1 ; Дельта Y
lab20: call ball1 ; Построение светлого мяча
push cx
mov cx,5000 ; Задержка
lab21: loop lab21
pop cx
call ball0 ; Стирание мяча
cmp dl,79
jnz lab22
neg cl
jmp lab23
lab22: cmp dl,0
jnz lab23
neg cl
lab23: add dl,cl
cmp dh,99
jnz lab24
neg ch
jmp lab25
lab24: cmp dh,0
jnz lab25
neg ch
lab25: add dh,ch
jmp lab20
ret
lab2 endp
ball1 proc near ; Рисование мяча
mov ax,0b800h
mov es,ax
mov ax,80
mul dh ; 80 * Y
xor bh,bh
mov bl,dl
add bx,ax ; 80 * Y + X
mov es:byte ptr [bx],3ch
add bx,2000h
mov es:byte ptr [bx],0ffh
sub bx,2000h-80
mov es:byte ptr [bx],0ffh
add bx,2000h
mov es:byte ptr [bx],3ch
ret
ball1 endp
ball0 proc near ; Стирание мяча
mov ax,0b800h
mov es,ax
mov ax,80
mul dh ; 80 * Y
xor bh,bh
mov bl,dl
add bx,ax ; 80 * Y + X
mov es:byte ptr [bx],0
add bx,2000h
mov es:byte ptr [bx],0
sub bx,2000h-80
mov es:byte ptr [bx],0
add bx,2000h
mov es:byte ptr [bx],0
ret
ball0 endp
lab1 proc near
mov ax,cs
mov ds,ax ; DS = CS
mov ax,0b800h
mov es,ax ; ES = B800h
xor si,si ; Обнуление регистра-источника
xor di,di ; Обнуление регистра назначения
mov cx,screen_size/2 ; Инициализация счетчика
mov al,color ; Цвет
rep stosb
ret
lab1 endp
Code ENDS
END Start
2.4 РАБОТА СО ЗВУКОМ
2.4.1 Извлечение звука в IBM PC
В простейшем случае в IBM PC для генерации звука используется микросхема интегрального таймера 8253 или 8254. Эта микросхема имеет три независимых канала, каждый из которых может программироваться для работы в режиме делителя частоты или генератора одиночных импульсов. Каждый канал содержит 16-разрядный счетчик, в который записывается значение делителя частоты или коэффициента пересчета (в зависимости от режима работы). Каждый канал имеет вход частоты (clk) и вход разрешения (gate). На вход частоты всех каналов подается импульсный сигнал частотой 1,19 МГц. Канал 0 микросхемы таймера используется для выработки сигнала прерывания по таймеру (частотой 18,2 Гц). Канал 1 работает в режима генерации одиночных импульсов через каждые 15 мкс. Этот сигнал используется для регенерации динамической памяти ЭВМ.
Порт | Таймер | |||||||||||
Прерывание от | ||||||||||||
таймера | ||||||||||||
clk0 | out0 | 18,2 kHz | IRQ0 | |||||||||
gate0 | Регенератор памяти | |||||||||||
clk1 | out1 | 15 мкс | DRQ0 | |||||||||
1,19 | gate1 | |||||||||||
MHz | clk2 | out2 | ||||||||||
gate2 | ||||||||||||
pb: | 61h | & | ||||||||||
pb1 | Speaker | |||||||||||
pb0 | ||||||||||||
Схема ггенерации звука в IBM PC | ||||||||||||
Канал 2 микросхемы исходно программируется для работы в режиме делителя частоты. Выход канала используется для генерации звука через встроенный динамик. Для управления звуком используются биты 0 и 1 системного порта В (микросхема 8255). Бит 0 используется для разрешения прохождения сигнала на выход канала 2 таймера. Сигнал с выхода канала 2 подается на схему "И", на второй вход который подается сигнал бита 1 системного порта В. Этот сигнал может разрешать или запрещать прохождения сигнала с выхода канала 2 таймера, а при закрытом канале 2 (битом 0 порта В) сигнал бита 1 порта В может использоваться для непосредственной генерации звука в динамике.
Адрес системного порта В - 61h, адреса каналов таймера - 40h, 41h, 42h, 43h - для каналов 0, 1, 2 и управляющего регистра соответственно. Ниже рассмотрены примеры генерации звука с помощью сигнала бита 1 системного порта В, а также с помощью таймера. Рассмотрен случай извлечения звука с использованием прерывания.
2.4.2 Примеры генерации звука
Задача 2.4.2.1. Написать программу, издающую различные звуки при нажатии на клавиши '1' и '2'. Для генерации звука следует использовать сигнал бита 1 системного порта В. Выход из программы должен осуществляться по нажатию клавиши 'q'.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
frequency1 equ 300 ; Задержка переключения 1
frequency2 equ 500 ; Задержка переключения 2
number_cycles1 equ 1000 ; Количество циклов (длит.)
number_cycles2 equ 600 ; Количество циклов (длит.)
port_b equ 61h ; Адрес системного порта В
.286
Start proc near
mov ax,cs
mov ds,ax ; DS = CS
beg1: call kbin ; Опрос клавиатуры
cmp al,'1' ; = '1' ?
jnz beg2 ; Нет
call ton1 ; Звук высоты 1
jmp beg1 ; Переход на начало цикла
beg2: cmp al,'2' ; = '2' ?
jnz beg3 ; Нет
call ton2 ; Звук высоты 2
jmp beg1 ; Переход на начало цикла
beg3: cmp al,'q' ; = 'q' ?
jnz beg1 ; Нет
int 20h ; Выход из программы
start endp
ton2 proc near ; Процедура генерации звука 2
mov dx,number_cycles2 ; Длительность 2
mov di,frequency2 ; Задержка 2
jmp ton0 ; Переход на универсальную процедуру генерации звука
ton1 proc near ; Процедура генерации звука 1
mov dx,number_cycles1 ; Длительность 1
mov di,frequency1 ; Задержка 1
; Универсальная процедура генерации звука
; DX - количество циклов, DI - задержка
ton0 proc near
cli ; Запрещение прерываний
in al,port_b ; Чтение сост. системн. порта В
and al,11111110b ; Отк. динамика от таймера
ton01: or al,00000010b ; Включение динамика
out port_b,al ; Запись в системный порт В
mov cx,di ; Счетчик цикла задержки
loop $ ; Задержка
; Выключение звука
and al,11111101b ; Выключение динамика
out port_b,al ; Запись в системный порт В
mov cx,di ; Счетчик цикла задержки
loop $ ; Задержка
dec dx ; Декремент счетчика колич. циклов
jnz ton01 ; Переход на начало нового периода
sti ; Разрешение прерываний
ret ; Выход из процедуры
ton0 endp ; Конец универсальной процедуры
ton1 endp ; Конец процедуры генерации звука 1
ton2 endp ; Конец процедуры генерации звука 2
kbin proc near ; Ввод с клавиатуры с ожиданием
mov ah,0 ; Функция 0
int 16h ; клавиатурного прерывания
ret ; Выход из процедуры
kbin endp ; Конец процедуры ввода с клавиатуры
code ends ; Конец сегмента (кодового)
END Start ; Указание на точку входа
Задача 2.4.2.2. Написать программу, издающую различные звуки при нажатии на клавиши '1' и '2'. Для генерации звука следует выход канала 2 таймера. Выход из программы должен осуществляться по нажатию клавиши 'q'.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
frequency1 equ 1000 ; Коэффиц. деления 1
frequency2 equ 3000 ; Коэффиц. деления 2
duration equ 50000 ; Длительность
port_b equ 61h ; Адрес системного порта В
.286
Start proc near ; Основная процедура
mov ax,cs
mov ds,ax ; DS = CS
beg1: call kbin ; Опрос клавиатуры
cmp al,'1' ; = '1' ?
jnz beg2 ; Нет
call ton1 ; Звук высоты 1
jmp beg1 ; Переход на начало цикла
beg2: cmp al,'2' ; = '2' ?
jnz beg3 ; Нет
call ton2 ; Звук высоты 2
jmp beg1 ; Переход на начало цикла
beg3: cmp al,'q' ; = 'q' ?
jnz beg1 ; Нет
int 20h ; Выход из программы
start endp ; Конец основной процедуры
ton2 proc near ; Процедура генерации звука 2
mov dx,duration ; Длительность
mov di,frequency2 ; Коэффициент деления 2
jmp ton0 ; Переход на универсальную процедуру
ton1 proc near ; Процедура генерации звука 1
mov dx,duration ; Длительность
mov di,frequency1 ; Коэффициент деления 2
; Универсальная процедура генерации звука
; DX - длительность, DI - коэффиц. деления
ton0 proc near
cli ; Запрещение прерываний
; Включение динамика и таймера
in al,61h ; Чтение состояния системного порта В
or al,3 ; Разрешение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
; Программирование делителя частоты 2 канала
mov ax,di ; Делитель частоты
out 42h,al ; Мл.байт частоты Þ канал 2 таймера
xchg al,ah ; AH Û AL
out 42h,al ; Ст.байт частоты Þ канал 2 таймера
; Формирование задержки
mov cx,dx ; Счетчик цикла задержки
ton01: push cx ; Команды, используемые только для
pop cx ; увеличения длит. цикла задержки
loop ton01 ; Задержка
; Выключение звука
in al,61h ; Чтение состояния системного порта В
and al,0fch ; Запрещение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
sti ; Разрешение прерываний
ret ; Выход из процедуры
ton0 endp ; Конец универсальной процедуры
ton1 endp ; Конец процедуры генерации звука 1
ton2 endp ; Конец процедуры генерации звука 2
kbin proc near ; Ввод с клавиатуры и проверка на выбор игры
; Процедура совпадает с одноименной в задаче 2.4.2.1.
kbin endp
code ends ; Конец сегмента (кодового)
END Start ; Указание на точку входа
Задача 2.4.2.3. Написать программу, издающую различные звуки при нажатии на клавиши '1' и '2'. Для генерации звука следует выход канала 2 таймера. Выход из программы должен осуществляться по нажатию клавиши 'q'. Использовать прерывание от таймера.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
.286
Start proc near ; Основная процедура
mov ax,cs
mov ds,ax ; DS = CS
jmp beg ; "Перескок" через переменные
frequency equ 0500h ; Коэффициент деления
iniflag db 0 ; Флаг звучания
old_int1c_off dw 0 ; Смещение старого вектора
old_int1c_seg dw 0 ; Сегмент старого вектора
beg: ; Сохранение старого вектора прерывания 1Сh
mov ax,35h ; Функция взятия вектора
mov al,1ch ; Вектор 1Сh
int 21h ; Вызов функции DOS
mov cs:old_int1c_off,bx ; Запись смещения
mov cs:old_int1c_seg,es ; Запись сегмента
; Установка в вектор прерывания адреса новой
; программы обработки прерывания
lea dx,new_int1c ; Запись нового вектора 1c
mov ah,25h ; Функция установки вектора прерыв.
mov al,1ch ; Номер вектора прерывания
int 21h ; DS:DX - адрес новой программы обр.
beg1: call kbin ; Опрос клавиатуры
cmp al,'1' ; = '1' ?
jnz beg2 ; Нет
mov byte ptr iniflag,1 ; Взведение флага звуч.
jmp beg1 ; Переход на начало цикла
beg2: cmp al,'2' ; = '2' ?
jnz beg3 ; Нет
mov byte ptr iniflag,0 ; Сброс флага звуч.
jmp beg1 ; Переход на начало цикла
beg3: cmp al,'q' ; = 'q' ?
jnz beg1 ; Нет
; Восстановление старого вектора 1с и выход
mov dx,old_int1c_off ; Смещение старого вектора
mov ax,old_int1c_seg ; Сегмент старого вектора
mov ds,ax ; DS:DX - адрес устанавл. вектора
mov ax,251ch ; Установка старого вектора 1ch
int 21h ; Вызов функции DOS
int 20h ; Выход из программы
start endp ; Конец основной процедуры
; Новый обработчик прерывания 1ch
new_int1c proc far ; Дальняя процедура
pusha ; Сохранение всех регистров (для Intel286)
call muz ; Вызов процедура извлечения звука
popa ; Восстановление всех регистров
iret ; Возврат из программы обработки прерывания
new_int1c endp ; Конец нового обработчика прерывания 1сh
muz proc near ; Процедура генерации звука
test byte ptr cs:iniflag,0ffh ; Проверка флага
jnz muz1 ; Продолжение
in al,61h ; Чтение системного порта В
and al,0fch ; Запрещение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
ret ; Выход, если флаг не взведен
muz1: ; Программирование делителя частоты 2 канала
mov ax,frequency ; Делитель частоты
out 42h,al ; Мл.байт частоты Þ канал 2 таймера
xchg al,ah ; AH Û AL
out 42h,al ; Ст.байт частоты Þ канал 2 таймера
; Разрешение звучания
in al,61h ; Чтение системного порта В
or al,3 ; Разрешение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
ret ; Нормальный выход
muz endp ; Конец процедуры генерации звука
kbin proc near ; Ввод с клавиатуры и проверка на выбор игры
; Процедура совпадает с одноименной в задаче 2.4.2.1.
kbin endp
code ends ; Конец сегмента (кодового)
END Start ; Указание на точку входа
Задача 2.4.2.4. Написать программу, исполняющую три различные мелодии при нажатии на клавиши '1', '2'и '3'. Для генерации звука следует использовать выход канала 2 таймера. Выход из программы должен осуществляться по нажатию клавиши 'q'. Использовать прерывание от таймера.
Для исполнения мелодии сначала формируется массив делителей частоты, соответствующих различным нотам звукоряда. Для нот используются номера от 1 до 48. Условное соответствие номеров нот и их значений показано на рисунке. Массив делителей частоты для нот называется NOTY. Значение 0 используется в качестве признака окончания мелодии. Значение 255 используется для обозначения паузы.
Номера нот и их соответствие | ||||||||||||||||||||||||||||||||||||||||
В программе сформированы массивы для трех мелодий - "Чижик Пыжик" (mel1), "Подмосковные вечера" и " Кан-кан" (mel3). Длительность элементарного звука берется равной одному "тику" таймера (примерно 1/18 сек.). Для получения большей длительности в массиве мелодии записываются подряд несколько одинаковых нот. Для получения четко выраженных соседних одинаковых нот используется пауза.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
.286
Start proc near ; Основная процедура
mov ax,cs
mov ds,ax ; DS = CS
jmp beg ; "Перескок" через переменные
iniflag db 0 ; Флаг звучания
old_int1c_off dw 0 ; Смещение старого вектора
old_int1c_seg dw 0 ; Сегмент старого вектора
tek_mel dw ? ; Адрес текущей ноты выбранной мелодии
beg: mov ax,351ch ; Сохранение старого вектора 1c
int 21h
mov cs:old_int1c_off,bx ; Запись смещения
mov cs:old_int1c_seg,es ; Запись сегмента
lea dx,new_int1c ; Запись нового вектора 1c
mov ah,25h ; Функция установки вектора прерыв.
mov al,1ch ; Номер вектора прерывания
int 21h ; DS:DX - адрес новой программы обр.
beg1: call kbin ; Опрос клавиатуры
cmp al,'1' ; = '1' ?
jnz beg2 ; Нет
mov byte ptr iniflag,1 ; Взведение флага звуч.
lea ax,mel1
mov tek_mel,ax
jmp beg1 ; Переход на начало цикла
beg2: cmp al,'2' ; = '2' ?
jnz beg3 ; Нет
mov byte ptr iniflag,1 ; Сброс флага звуч.
lea ax,mel2
mov tek_mel,ax
jmp beg1 ; Переход на начало цикла
beg3: cmp al,'3' ; = '3' ?
jnz beg4 ; Нет
mov byte ptr iniflag,1 ; Сброс флага звуч.
lea ax,mel3
mov tek_mel,ax
jmp beg1 ; Переход на начало цикла
beg4: cmp al,'q' ; = 'q' ?
jnz beg1 ; Нет
; Восстановление старого вектора 1с и выход
mov dx,old_int1c_off ; Смещение старого вектора
mov ax,old_int1c_seg ; Сегмент старого вектора
mov ds,ax ; DS:DX - адрес устанавл. вектора
mov ax,251ch ; Установка старого вектора 1ch
int 21h
int 20h
start endp
; Новый обработчик прерывания 1ch
new_int1c proc far ; Дальняя процедура
pusha ; Сохранение всех регистров
call muz ; Вызов процедура извлечения звука
popa ; Восстановление всех регистров
iret ; Возврат из программы обработки прерывания
new_int1c endp
; Процедура извлечения очередного звука
; tek_mel - адрес текущей ноты выбранной мелодии
muz proc near
test byte ptr cs:iniflag,0ffh ; Проверка флага
jnz muz1 ; Продолжение
muze: in al,61h ; Чтение состояния системного порта В
and al,0fch ; Запрещение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
ret ; Выход, если флаг не взведен
muz1: mov si,cs:tek_mel ; Адрес текущей ноты
mov bl,cs:[si] ; Текущая нота
cmp bl,255 ; Пауза ?
jnz muz2
; Выключение звука
in al,61h ; Чтение состояния системного порта В
and al,0fch ; Запрещение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
inc cs:tek_mel ; Переход к адресу след. ноты
ret
muz2: or bl,bl ; = 0 ?
jnz muz3 ; Продолжение
jmp muze ; Выход, если признак конца
muz3: shl bl,1 ; Умножение bl на 2
xor bh,bh ; bh = 0
mov ax,cs:noty[bx] ; В DI частота ноты
; Программирование делителя частоты 2 канала
inc cs:tek_mel ; Переход к адресу след. ноты
out 42h,al ; Мл.байт частоты Þ канал 2 таймера
xchg al,ah ; AH Û AL
out 42h,al ; Ст.байт частоты Þ канал 2 таймера
; Разрешение звучания
in al,61h ; Чтение состояния системного порта В
or al,3 ; Разрешение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
ret ; Нормальный выход
muz endp
kbin proc near ; Ввод с клавиатуры и проверка на выбор игры
; Процедура совпадает с одноименной в задаче 2.4.2.1.
kbin endp
; Мелодия "Чижик Пыжик"
mel1 db 17,17,255,13,13,255,17,17,255,13,13,255,18,18,255
db 17,17,255,15,15,15,15,255,255
db 8,8,255,8,8,255,8,8,255,10,255,12,255
db 13,13,255,13,13,255,13,13,13,13
db 0
; Мелодия "Подмосковные вечера"
mel2 db 1,1,1,4,4,4,8,8,8,4,4,4,6,6,6,6,6,6,4,4,4,3,3,3
db 8,8,8,8,8,8,6,6,6,6,6,6,1,1,1,1,1,1,1,1,1,1,1,1
db 0
; Мелодия "Кан-кан"
mel3 db 18,6,25,13,22,18,25,13,20,1,23,8,22,5,20,1
db 25,6,13,13,25,10,13,13,25,10,27,13,22,10,23,13,20,1
db 11,11,20,5,11,11,20,1,23,11,22,5,20,11,18,6,30,18
db 29,17,27,15,25,13,23,11,22,10,20,8,18,6,13,13,18,10
db 13,13,20,1,23,8,22,5,20,8,25,6,13,13,25,10,13,13
db 25,6,27,13,22,10,23,13,20,1,8,8,20,5,8,8,20,1,23,8
db 22,5,20,8,18,6,25,10,20,13,22,10,18,6,6,6,6,6,6,6
db 34,8,24,12,24,15,34,12,32,1,25,5,25,8,29,5,30,6,34,13
db 37,10,34,13,34,1,32,8,32,5,8,8,34,8,24,12,24,15,34,12
db 32,1,25,5,25,8,29,5,29,3,27,7,29,10,27,13,34,12,32,8
db 34,6,32,3,34,8,24,15,24,12,34,15,32,1,29,8,25,5,29,8
db 30,6,34,13,37,10,34,13,34,1,32,5,32,8,5,5,34,8,24,15
db 24,12,34,15,32,1,25,6,25,5,29,8,29,3,27,7,29,10,27,7
db 32,8,30,6,29,5,27,3,25,1,8,8,25,5,8,8,27,12,30,15
db 29,8,27,12,32,1,8,8,32,5,8,8,32,1,34,8,29,5,30,8,27,8
db 15,15,27,12,15,15,27,8,30,12,29,15,27,12,25,1,37,1
db 36,5,34,6,32,8,30,8,29,10,27,12,25,1,8,8,25,5,8,8
db 27,8,30,15,29,12,27,15,32,1,8,8,32,5,8,8,32,1,34,8
db 29,5,30,8,27,8,15,15,27,12,15,15,27,8,30,15,29,12
db 27,15,25,1,32,8,27,5,29,8,25,1,32,32,37,37,0
; Коэффициенты деления для нот
noty dw 0eeeh,0e18h,0d49h,0c8eh,0bdfh,0b2fh,0abeh
dw 9f7h,968h,8e0h,861h,7e8h,777h,70ch,6a5h,647h
dw 5edh,597h,547h,4fbh,4b4h,470h,430h,3f4h
dw 3bbh,386h,352h,323h,2f6h,2cbh,2a3h,27dh,25ah,238h,218h,1fah
dw 1ddh,1c3h,1a9h,192h,17bh,166h,152h,13fh,12dh,11ch,10ch,0fdh
dw 0
code ends
END Start
Задача 2.4.2.5. Модифицировать программу из предыдущей задачи так, чтобы длительность каждого звука задавалась в массиве мелодии. Для каждого звука должна записываться пара значений: номер ноты и длительность, выраженная в элементарных "тиках" таймера. Для простоты в программе будут реализованы только первые две короткие мелодии.
Assume CS: Code, DS: Code
Code SEGMENT
org 100h
.286
Start proc near
mov ax,cs
mov ds,ax
jmp beg
pausa equ 255
frequency dw 1000h
iniflag db 0 ; Флаг звучания
old_int1c_off dw 0 ; Смещение старого вектора
old_int1c_seg dw 0 ; Сегмент старого вектора
tek_mel dw ?
duration db 0 ; Длительность текущей ноты
beg: mov ax,351ch ; Сохранение старого вектора 1c
int 21h
mov cs:old_int1c_off,bx ; Запись смещения
mov cs:old_int1c_seg,es ; Запись сегмента
lea dx,new_int1c ; Запись нового вектора 1c
mov ah,25h ; Функция установки вектора прерыв.
mov al,1ch ; Номер вектора прерывания
int 21h ; DS:DX - адрес новой программы обр.
beg1: call kbin ; Опрос клавиатуры
mov byte ptr duration,1
cmp al,'1' ; = '1' ?
jnz beg2 ; Нет
mov byte ptr iniflag,1 ; Взведение флага звуч.
lea ax,mel1
mov tek_mel,ax
jmp beg1 ; Переход на начало цикла
beg2: cmp al,'2' ; = '2' ?
jnz beg3 ; Нет
mov byte ptr iniflag,1 ; Сброс флага звуч.
lea ax,mel2
mov tek_mel,ax
jmp beg1 ; Переход на начало цикла
beg3: cmp al,'q' ; = 'q' ?
jnz beg1 ; Нет
; Восстановление старого вектора 1с и выход
mov dx,old_int1c_off ; Смещение старого вектора
mov ax,old_int1c_seg ; Сегмент старого вектора
mov ds,ax ; DS:DX - адрес устанавл. вектора
mov ax,251ch ; Установка старого вектора 1ch
int 21h
int 20h
start endp
; Новый обработчик прерывания 1ch
new_int1c proc far
pusha
dec byte ptr cs:duration
jnz ex
call muz ; Вызов процедуры извлечения звука
ex: popa
iret
new_int1c endp
muz proc near
test byte ptr cs:iniflag,0ffh ; Проверка флага
jnz muz1 ; Продолжение
muze: in al,61h ; Чтение состояния системного порта В
and al,0fch ; Запрещение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
ret ; Выход, если флаг не взведен
muz1: mov si,cs:tek_mel ; Адрес текущей ноты
mov bx,word ptr cs:[si] ; BL -текущая нота, BH - длительность
mov cs:duration,bh ; Длит. в системную переменную
cmp bl,255 ; Пауза ?
jnz muz2
; Выключение звука
in al,61h ; Чтение состояния системного порта В
and al,0fch ; Запрещение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
inc cs:tek_mel ; Переход к адресу след. ноты
inc cs:tek_mel ; след. длительности
ret
muz2: or bl,bl ; = 0 ?
jnz muz3 ; Продолжение
jmp muze ; Выход, если признак конца
muz3: shl bl,1 ; Умножение bl на 2
xor bh,bh ; bh = 0
mov ax,cs:noty[bx] ; В DI частота ноты
; Программирование делителя частоты 2 канала
inc cs:tek_mel ; Переход к адресу след. ноты
inc cs:tek_mel ; и след. длительности
out 42h,al ; Мл.байт частоты Þ канал 2 таймера
xchg al,ah ; AH Û AL
out 42h,al ; Ст.байт частоты Þ канал 2 таймера
; Разрешение звучания
in al,61h ; Чтение состояния системного порта В
or al,3 ; Разрешение звучания (биты 0 и 1)
out 61h,al ; Запись в системный порт В
ret ; Нормальный выход
muz endp
kbin proc near ; Ввод с клавиатуры
; Процедура совпадает с одноименной в задаче 2.4.2.1.
kbin endp
mel1 db 17,2,255,1,13,2,255,1,17,2,255,1,13,2,255,1,18,2,255,1, 17,2
db 255,1,15,4,255,2, 8,2,255,1,8,2,255,1,8,2,255,1,10,1,255,1
db 12,1,255,1,13,2,255,1,13,2,255,1,13,4,0
mel2 db 1,4,4,4,8,4,4,4,6,8,4,4,3,4,8,8,6,8,1,12,0
noty dw 0eeeh,0e18h,0d49h,0c8eh,0bdfh,0b2fh,0abeh, 9f7h,968h
dw 8e0h,861h,7e8h,777h,70ch,6a5h,647h, 5edh,597h,547h
dw 4fbh,4b4h,470h,430h,3f4h, 3bbh,386h,352h,323h,2f6h,2cbh
dw 2a3h,27dh,25ah,238h,218h,1fah, 1ddh,1c3h,1a9h,192h,17bh
dw 166h,152h,13fh,12dh,11ch,10ch,0fdh,0
code ends
END Start