Мелодия из «Призрака оперы» через системный динамик — классическая учебная задача на MASM: программа напрямую программирует канал 2 таймера Intel 8253/8254 через порты 42h и 43h, а звук включается установкой битов порта 61h. Если ваша программа молчит, чаще всего причина в том, что забыт стробирующий бит в порте 61h либо делитель частоты записан в неверном порядке — сначала младший байт, затем старший.

В этой статье разберём принцип генерации тона, структуру таблицы нот для фразы из «Призрака оперы», типовой каркас программы на MASM и типичные ошибки, из-за которых спикер молчит или играет «мимо» нот. Материал ориентирован на DOS-окружение (реальное или в DOSBox), поскольку прямой доступ к портам в современных Windows приложениям запрещён.

Как PC-спикер превращает числа в звук

Системный динамик подключён к каналу 2 программируемого интервального таймера. Таймер получает базовую частоту примерно 1,193 МГц (точное значение — 1193180 Гц), и вы задаёте делитель: частота ноты равна 1193180, делённому на этот делитель. Например, для ноты Ля первой октавы (440 Гц) делитель будет примерно 2712.

Последовательность действий строго фиксирована. Сначала в порт 43h записывается управляющее слово, выбирающее канал 2 и режим генератора прямоугольных импульсов. Затем в порт 42h дважды выводится делитель — младший байт, потом старший. Наконец, в порт 61h записывается значение с установленными двумя младшими битами, которые подключают выход таймера к динамику.

💡

Частота ноты = 1193180 / делитель. Делитель всегда пишется в порт 42h двумя байтами: сначала младший, затем старший.

Таблица нот для «Призрака оперы»

Узнаваемая фраза темы Эндрю Ллойда Уэббера строится на последовательности полутонов с характерным «криком» вверх. Для программы удобно хранить не частоты, а сразу делители таймера — так код проигрывания становится тривиальным. Ниже ориентировочная таблица основных нот, которые понадобятся для мелодии.

НотаЧастота, ГцДелитель (примерно)Комментарий
Ре (D)2944058Начальный тон фразы
До диез (C#)2774308Полутон вниз
До (C)2624554Продолжение спуска
Си (B)2474831Низкая опора
Ля (A)4402712Опорная нота для проверки

Длительности хранятся отдельным массивом — в «тиках» вашей процедуры задержки. Проще всего завести базовую единицу (например, восьмую ноту) и задавать длительности кратно ей: 1, 2, 4. Точный темп подбирается на слух, потому что скорость программной задержки зависит от процессора и среды исполнения.

Почему делители указаны примерно

Делитель — целое число, поэтому большинство нот получаются с небольшой погрешностью в доли герца. Для PC-спикера и человеческого слуха это несущественно, но при желании можно округлять делитель в ближайшую сторону и проверять результат тюнером.

Каркас программы на MASM

Ниже — упрощённый фрагмент, показывающий суть: включение канала таймера и вывод делителя. Это учебный пример для DOS-окружения; полная программа дополнительно содержит цикл по массиву нот и процедуру задержки.

; Включение звука заданной частоты

; BX = делитель таймера

sound_on proc

mov al, 0B6h ; канал 2, младший+старший байт, режим 3

out 43h, al

mov ax, bx

out 42h, al ; младший байт делителя

mov al, ah

out 42h, al ; старший байт делителя

in al, 61h ; подключаем таймер к динамику

or al, 03h

out 61h, al

ret

sound_on endp

Выключение звука — зеркальная операция: читаем порт 61h, сбрасываем два младших бита командой and al, 0FCh и записываем обратно. Забыть это нельзя: иначе динамик будет гудеть даже после выхода из программы, пока машину не перезапустят.

Основной цикл проигрывания выглядит так: берём очередной делитель из массива нот, вызываем sound_on, ждём заданное число единиц задержки, при необходимости вставляем короткую паузу между нотами (выключаем звук на долю длительности), переходим к следующему элементу. Признак конца мелодии удобно кодировать нулевым делителем.

Задержки: почему мелодия «плывёт»

Классическая проблема учебных программ — задержка пустым циклом вида loop $. Её длительность зависит от тактовой частоты процессора: на быстрой машине мелодия превращается в трель, на медленной — тянется бесконечно. В DOSBox эмулируемая скорость задаётся настройкой cycles, поэтому одна и та же программа звучит по-разному.

Более надёжные варианты:

  • ⏱️ Использовать системный тик таймера — обновление счётчика в области BIOS по адресу 0040:006C происходит примерно 18,2 раза в секунду.
  • 🎵 Перепрограммировать канал 0 таймера на более частые прерывания — точнее, но требует аккуратного восстановления состояния при выходе.
  • 🔁 Оставить программный цикл, но подобрать константу под конкретную среду и зафиксировать cycles в DOSBox.
⚠️ Внимание: если ваша программа меняет частоту канала 0 таймера или перехватывает прерывания, обязательно восстанавливайте исходные значения перед завершением. Иначе DOS-сессия после выхода будет вести себя непредсказуемо — от «замедления» клавиатуры до зависания.

Типичные ошибки и диагностика

Когда программа собирается без ошибок, но спикер молчит, проверяйте по цепочке от простого к сложному. Первым делом убедитесь, что среда вообще эмулирует спикер: в DOSBox звук динамика включён по умолчанию, но громкость зависит от микшера эмулятора и системы.

  • 🔇 Нет звука вообще — проверьте, что в порт 61h записано значение с битами 0 и 1 установленными, и что управляющее слово 0B6h ушло в порт 43h до записи делителя.
  • 🎼 Нота есть, но «не та» — скорее всего, перепутан порядок байтов делителя или в таблице стоят частоты вместо делителей.
  • ⏩ Мелодия играет слишком быстро или медленно — проблема в задержке, а не в нотах; см. раздел про таймер.
  • 📢 Динамик гудит после выхода — забыто отключение битов порта 61h в конце программы.

☑️ Проверка перед запуском мелодии

Выполнено: 0 / 6
📊 Где вы запускаете свою MASM-программу?
DOSBox
Реальный DOS на старом ПК
Эмулятор (86Box, PCem)
Учебная виртуальная машина

Сборка и запуск

Для сборки понадобятся сам MASM (или совместимый ассемблер — TASM в режиме совместимости, JWASM/UASM) и компоновщик, умеющий создавать DOS-исполняемые файлы. Классическая последовательность для COM- или EXE-формата выглядит так:

ml /c phantom.asm

link phantom.obj;

Точные ключи зависят от версии ассемблера и выбранной модели памяти — сверяйтесь с документацией вашего инструментария. COM-формат проще для маленьких программ (код начинается с org 100h), EXE удобнее при нескольких сегментах данных. Запускать результат следует в DOSBox или на реальном DOS: в Windows программа с прямым доступом к портам просто не выполнится корректно.

💡

Начните отладку с одной непрерывной ноты Ля (делитель около 2712), звучащей пару секунд. Как только она стабильно играет и гаснет — переходите к массиву нот и ритму. Так вы отделите ошибки генерации звука от ошибок логики мелодии.

Ограничения подхода и что дальше

PC-спикер воспроизводит только один голос прямоугольной формы — никакой полифонии и тембра. Для «Призрака оперы» это даже аутентично: драматичная монофоническая тема звучит узнаваемо. Но если хочется большего, следующие шаги очевидны: звук через AdLib/Sound Blaster (FM-синтез или PCM), где программирование идёт уже через другие порты и требует инициализации карты.

⚠️ Внимание: прямой доступ к портам — привилегированная операция. В DOS это нормально, но не пытайтесь переносить этот код в Windows-приложение: доступ к оборудованию там возможен только через драйверы, и попытка выполнить out из пользовательского режима завершится ошибкой защиты.

Из учебной задачи «Призрак оперы» на MASM вырастает вполне практический навык: работа с портами ввода-вывода, таймерами, табличными данными и аккуратным завершением программы. Это та же база, что нужна для драйверов, демосцены и низкоуровневой диагностики железа.

💡

Формула успеха: правильное управляющее слово в 43h, делитель двумя байтами в 42h, биты 0–1 порта 61h включены перед звуком и выключены после. Всё остальное — таблица нот и ритм.

Частые вопросы

Почему программа работает в DOSBox, но молчит в Windows?

Современные Windows запрещают приложениям прямой доступ к портам ввода-вывода. Инструкции in/out из пользовательского режима вызывают исключение. Используйте DOSBox, DOS-виртуальную машину или реальное DOS-железо.

Как подобрать делитель для любой ноты?

Разделите 1193180 на частоту ноты в герцах и округлите до целого. Таблицы частот всех нот равномерно темперированного строя есть в любой справочной литературе по музыкальной акустике.

Можно ли играть аккорды на PC-спикере?

Аппаратно канал один, поэтому настоящей полифонии нет. Существуют программные приёмы (быстрое чередование нот, PWM-подобные техники), но это существенно сложнее и выходит за рамки базовой задачи.

Чем отличается COM от EXE для такой программы?

COM — плоский образ до 64 Кбайт, загружается со смещения 100h, все сегментные регистры равны. EXE имеет заголовок и отдельные сегменты кода, данных и стека. Для мелодии на пару сотен байт COM проще, но EXE привычнее при использовании сегментных директив MASM.

Динамик продолжает гудеть после выхода — что делать?

Ваша программа не сбросила биты порта 61h. Добавьте в конец: in al, 61h, затем and al, 0FCh, затем out 61h, al. До исправления поможет перезагрузка машины или сброс DOSBox.