В) Использование команды 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

Наши рекомендации