≡ Передовица » Макулатура » ИиО » Агат, ассемблер, музыка.
Агат, ассемблер, музыка. (N5/1989)М. Алексеев, А. Рябова. г. Миасс. Группа сайта просит вас связаться с нами! (ЗАЧЕМ ЭТО?) Наверное, многие согласятся, что одно из наиболее ярких впечатлений от первого знакомства с ПЭВМ "Агат" оставляет исполнение тест-программой мелодии Баха. А как самим запрограммировать мелодию? Ведь в штатном Бейсике "Агата" нет соответствующих операторов. Но нет худа без добра - это прекрасный повод поближе узнать машину и язык ассемблера. За динамик в "Агате" отвечает ячейка с адресом ¤C030. При каждом обращении к ней на чтение или запись на динамик подаётся короткий импульс напряжения, и, если это делать в цикле - должен получиться звук определённой частоты. Но в цикле Язык ассемблера - мощный и гибкий инструмент, позволяющий реализовать буквально любой замысел. Мы же воспользуемся лишь несколькими простейшими ассемблерными командами, подобными, кстати, командам программируемого калькулятора. Они описывают операции с объявленными однобайтовыми ячейками и регистрами A, X, Y. Так, по команде LDX содержимое указанной ячейки помещается в X, а по команде STA в указанную ячейку помещается содержимое A. Для уменьшения содержимого ячейки на 1 предназначена команда DEC, то же делают с X и Y команды DEX и DEY. Понадобятся нам также команды ветвления: BNE - условный переход на указанную метку в случае ненулевого результата предыдущей операции, BEQ - то же при нулевом результате; RTS - возврат в вызывающую программу. Входными параметрами нашей первой простейшей программы будут ПЕРИОД - условный интервал между обращениями к динамику, соответствующий периоду звуковых колебаний, и ДЛИНА - условная продолжительность звука, соответствующая числу периодов (русские буквы выбраны для наглядности, возможно, кто-то предпочтёт имена типа DLINA или LENGTH). Место для размещения параметров и программы в памяти следует выбрать так, чтобы не испортить систему и свои данные. Возьмём, например, адрес ¤2000. 10 ЗВУК=¤2000 20 ПЕРИОД=ЗВУК-1 30 ДЛИНА=ЗВУК-2 70 * ЗВУК : REM алг ЗВУК(цел ПЕРИОД, ДЛИНА) 80 REM нач 120 REM пока ДЛИНА>0 130 ! ДИНАМИК: LDA ¤C030 ; нц импульс 140 ! LDX ПЕРИОД ; для X от ПЕРИОД до 1 шаг -1 150 ! ЦИКЛ :DEX ; нц 160 ! BNE ЦИКЛ ; кц 170 ! DEC ДЛИНА ; ДЛИНА=ДЛИНА-1 250 ! BNE ДИНАМИК ; кц 260 ! RTS ; кон 270 ! : 300 РОКЕ ПЕРИОД, 255 310 РОКЕ ДЛИНА, 255 320 CALL ЗВУК Необходимо иметь в виду различное использование имён в Бейсике и ассемблере. Например, в Бейсике ПЕРИОД - это переменная, в которую заносится число ¤1FFF, а в ассемблере ПЕРИОД - это ячейка, расположенная по адресу ¤1FFF, куда перед вызовом ЗВУКа необходимо оператором РОКЕ занести соответствующий байт. Комбинируя обращения к подпрограмме ЗВУК с различными входными параметрами, уже можно пытаться создавать шумовые эффекты. Однако для исполнения мелодий она неудобна, так как продолжительность исполняемых ею звуков зависит от высоты. Для исправления этого недостатка в новой версии будем измерять продолжительность звука не периодами, длительность которых отсчитывается уменьшением содержимого регистра X на 1 в цикле от значения, записанного в ячейку ПЕРИОД, до 0, а независимыми от входных параметров интервалами, равными максимально возможному периоду (256) и отсчитываемыми уменьшением Y в цикле от 0 (256) до 0 (дело в том, что если Y=0, то команда DEY делает его равным 255). Ещё один недостаток - несохранение исходного значения параметра ДЛИНА. Это довольно неудобно, поэтому заведём рабочую ячейку ДЛ. 10 ЗВУК=¤2000 20 ПЕРИОД=ЗВУК-1 30 ДЛИНА=ЗВУК-2 40 ДЛ=ЗВУК-3 70 * ЗВУК : REM алг ЗВУК(цел ПЕРИОД, ДЛИНА) 80 REM нач 100 ! LDY #0 ; Y=0 (Y=256) 110 ! LDA ДЛИНА ; ДЛ=ДЛИНА 120 ! STA ДЛ ; пока ДЛ>0 130 ! ДИНАМИК :LDA ¤C030 ; нц импульс 140 ! LDX ПЕРИОД ; для X от ПЕРИОД до 1 шаг -1 150 ! ЦИКЛ :DEY ; нц Y=Y-1 160 ! BNE СЧЕТХ ; если Y=0 то (Y=256) 210 ! DEC ДЛ ; ДЛ=ДЛ-1 220 ! ВЕQ ВЫХОД ; если ДЛ=0 то выход все 230 ! СЧЕТХ :DEX ; все 240 ! BNE ЦИКЛ ; кц 250 ! BEQ ДИНАМИК ; кц 260 ! ВЫХОД :RTS ; кон 270 ! : 300 РОКЕ ПЕРИОД, 255 310 РОКЕ ДЛИНА, 255 320 CALL ЗВУК В таком виде программа уже годится для исполнения быстрых мелодий, но генерировать долгие звуки она не способна. Всё происходит слишком быстро даже при максимальном значении (256) однобайтового параметра ДЛИНА. Введём ещё один параметр - МНОЖИТЕЛЬ - для увеличения длительности звука, определяемой параметром ДЛИНА, в заданное число раз, и рабочую ячейку МН. Программа дополнится такими строками: 50 МНОЖИТЕЛЬ=ЗВУК-4 60 МН=ЗВУК-5 290 РОКЕ МНОЖИТЕЛЬ,16 80 ! LDA МНОЖИТЕЛЬ ; нач 90 ! STA МН ; МН=МНОЖИТЕЛЬ Тело цикла станет таким: 160 ! BNE СЧЕТХ ; если Y=0 то (Y=256) 170 ! DEC МН ; MH=MH-1 180 ! BNE СЧЕТХ ; если МН=0 то 190 ! LDA МНОЖИТЕЛЬ ; МН=МНОЖИТЕЛЬ 200 ! STA МН ; ДЛ=ДЛ-1 210 ! DEC ДЛ ; если ДЛ=0 то выход 220 ! BEQ ВЫХОД ; все 230 ! СЧЕТХ :DEX ; все Подобным образом можно увеличить и период колебаний, чтобы понизить частоту звука до басов и даже перестука, но мы пока перестанем совершенствовать программу и займёмся задачей извлечения "нефальшивых" звуков. Отношение частот соседних нот (полутон) равно корню 12-й степени из 2, значит, значения параметра ПЕРИОД должны принадлежать соответствующей геометрической прогрессии. Построим прогрессию на три октавы, взяв за начало 256 (0) - самый низкий звук. 300 DIM НОТА%(37) : HOTA%(1)=0 310 A=256 : P=2^(1/12) 320 FOR 1=2 ТО 37 330 A=A/P 340 НОТА%(I)=INT(А+0.5) 350 NEXT I Выбрав одну из нот в качестве опорной и отсчитывая от неё полутона вверх и вниз, можно закодировать мелодию в виде последовательности номеров нот - элементов массива прогрессии. Для каждого звука надо задать ещё и длительность, но, в связи с тем, что звуки одинаковой длины зачастую соседствуют, можно сэкономить, кодируя длительность отрицательным числом и считая, что она относится ко всем последующим нотам до новой длительности: -8 - одна восьмая, -16 - одна шестнадцатая и т. п. Если ко всем номерам нот добавлять одно и то же число, получится та же мелодия, но в другой тональности - выше или ниже. 400 СДВИГ=5 410 READ КОД 420 IF КОД<0 THEN РОКЕ ДЛИНА,-64/КОД : GOTO 410 430 КОД=КОД*СДВИГ 440 IF КОД>37 OR КОД<1 THEN END 450 РОКЕ ПЕРИОД, НОТА%(КОД) 460 CALL ЗВУК 470 GOTO 410 500 REM МАЛЕНЬКАЯ ЕЛОЧКА 510 DATA -8,8,-16,5,5,-8,8,-16,5,5,8,6,5,3,-4,1,-8, 10,-16,13,10,-8,8,-16,5,5,8,6,5,3,-4,1,256 Экспериментируя со сдвигом тональности, можно убедиться, что слишком высокие ноты звучат хуже, слышится фальшь, вызванная ошибками округления до целого. Для сравнения распечатайте члены прогрессии с округлением и без него. Работая с параметром МНОЖИТЕЛЬ, можно изменять темп исполнения - andante, allegro и т.п.; вставляя между всеми звуками небольшие паузы, можно получить staccato. Вообще кодирование паузы (видимо, нулём) и её отработку тоже обязательно надо предусмотреть. И собрать мелодии в библиотеку, и сделать каталог мелодий, и музыкальный редактор, и ещё, и ещё... Развивать эту тему можно бесконечно. Конечно, в некоторых версиях Бейсика есть специальные операторы SOUND и PLAY, а в Рапире - стандартные процедуры ЗВУК и НОТА, облегчающие программирование музыки. Но, овладев ассемблером, вы можете попытаться сымитировать звук выстрела, или удара, шум мотора и даже синтезировать речь. Желаем успехов! Литература Техническое описание ПЭВМ "Агат". Книга 2. Фr.3.032.002.T01-3. ППП "Школьница". Описания Рапиры и Отладчика. Морер У. Язык ассемблера для персонального компьютера Эпл. М.: Мир, 1987. * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |