Сборка 8086 - DOSBOX - Как сделать звуковой сигнал?

Я работаю над игрой «Саймон» в сборке. Мне нужно издавать звуковой сигнал всякий раз, когда включается кнопка, звуковые сигналы также должны отличаться друг от друга. спасибо


person Ron Baker    schedule 16.05.2017    source источник
comment
Возможный дубликат воспроизведения файлов .wav на устройстве DOSBox Sound Blaster   -  person Paul R    schedule 16.05.2017
comment
Вы обращались к справочнику по прерываниям DOS?   -  person Michael    schedule 16.05.2017


Ответы (1)


Вы можете использовать динамик, чтобы упростить дизайн.
Динамик позволяет воспроизводить прямоугольные волны с разной частотой. частоты, на самом деле его можно использовать для воспроизводить цифровой звук, но это более сложно.

Динамик — это просто электромагнит, когда через него проходит ток, он оттягивается назад, в противном случае он остается в исходном положении.
Перемещая динамик вперед и назад, можно создавать звуковые волны.

Динамик можно перемещать вручную или с помощью канала 2 PIT.
Бит 0 порта 61h управляет источником динамика (0 = вручную, 1 = PIT), а бит 1 того же порта является битом «включения динамика» при использовании PIT («положением» динамика, когда нет).

Вот схема (с этой страницы), отсутствующая часть ручного управления:

Схема динамика

PIT управляется через порт 40h-43h, мы будем использовать режим 3 (генератор прямоугольных импульсов), устанавливая каждый раз оба байта делителя.
Генератор PIT работает на частоте около 1,193180 МГц, делитель используется для управления период прямоугольной волны.
Не касаясь внутренностей: на каждом тике осциллятора PIT загруженный делитель уменьшается. Период прямоугольной волны равен времени, необходимому PIT для уменьшения делителя до нуля.

Создание звука — это просто вопрос программирования PIT с нужным делителем и включения динамика.
Через некоторое время нам нужно отключить его.
Самый простой способ сделать это — использовать int 1ch, который вызывается 18,2 раза в секунду.

Сохраняя продолжительность в переменной при первом воспроизведении звука, уменьшая ее на каждом такте int 1ch и отключая динамик, когда счетчик достигает нуля, можно контролировать продолжительность звукового сигнала.

Для использования int 1ch требуется функция настройки (beep_setup) и функция демонтажа (beep_teardown).

BITS 16

ORG 100h

__start__:

 ;Setup
 call beep_setup

 ;Sample beep of ~2sec
 mov ax, 2000
 mov bx, 36
 call beep_play

 ;Wait for input
 xor ax, ax
 int 16h

 ;Tear down
 call beep_teardown

 mov ax, 4c00h
 int 21h

 ;-------------------------------------------------

 ;
 ;Setup the beep ISR
 ;

 beep_setup:
  push es
  push ax

  xor ax, ax
  mov es, ax

  ;Save the original ISR
  mov ax, WORD [es: TIMER_INT * 4]
  mov WORD [cs:original_timer_isr], ax
  mov ax, WORD [es: TIMER_INT * 4 + 2]
  mov WORD [cs:original_timer_isr + 2], ax

  ;Setup the new ISR

  cli
  mov ax, beep_isr
  mov WORD [es: TIMER_INT * 4], ax
  mov ax, cs
  mov WORD [es: TIMER_INT * 4 + 2], ax
  sti

  pop ax
  pop es
  ret 


 ;
 ;Tear down the beep ISR
 ;

 beep_teardown:
  push es
  push ax

  call beep_stop

  xor ax, ax
  mov es, ax

  ;Restore the old ISR

  cli
  mov ax, WORD [cs:original_timer_isr]
  mov WORD [es: TIMER_INT * 4], ax
  mov ax, WORD [cs:original_timer_isr + 2]
  mov WORD [es: TIMER_INT * 4 + 2], ax
  sti

  pop ax
  pop es
  ret 

 ;
 ;Beep ISR
 ;
 beep_isr:
  cmp BYTE [cs:sound_playing], 0
  je _bi_end

  cmp WORD [cs:sound_counter], 0
  je _bi_stop

  dec WORD [cs:sound_counter]

 jmp _bi_end

_bi_stop:
  call beep_stop

_bi_end:
  ;Chain
  jmp FAR [cs:original_timer_isr]

 ;
 ;Stop beep
 ;
 beep_stop:
  push ax

  ;Stop the sound

  in al, 61h
  and al, 0fch    ;Clear bit 0 (PIT to speaker) and bit 1 (Speaker enable)
  out 61h, al

  ;Disable countdown

  mov BYTE [cs:sound_playing], 0

  pop ax
  ret

 ;
 ;Beep
 ;
 ;AX = 1193180 / frequency
 ;BX = duration in 18.2th of sec
 beep_play:
  push ax
  push dx

  mov dx, ax

  mov al, 0b6h
  out 43h, al

  mov ax, dx
  out 42h, al
  mov al, ah
  out 42h, al


  ;Set the countdown
  mov WORD [cs:sound_counter], bx

  ;Start the sound

  in al, 61h
  or al, 3h    ;Set bit 0 (PIT to speaker) and bit 1 (Speaker enable)
  out 61h, al


  ;Start the countdown

  mov BYTE [cs:sound_playing], 1

  pop dx
  pop ax
  ret

 ;Keep these in the code segment
 sound_playing      db  0
 sound_counter      dw  0
 original_timer_isr     dd  0

 TIMER_INT      EQU     1ch

Особая благодарность сепу Роланду за исправление ошибки в исходном коде!

Вы можете использовать beep_play для воспроизведения звукового сигнала, используемые единицы являются "естественными" единицами аппаратной конфигурации, описанной выше.
Если ваши частоты и продолжительность фиксированы, эти единицы упрощают код бесплатно.

Звуковой сигнал прекращается по истечении заданного времени, вы можете использовать beep_stop, чтобы остановить его принудительно.

Воспроизведение нескольких звуков одновременно невозможно (даже их микширование невозможно без использования методов PWM).
Вызов beep_play во время воспроизведения другого сигнала приведет к остановке текущего сигнала и запуску нового.

person Margaret Bloom    schedule 16.05.2017
comment
Отличный ответ. +1 Но у процедуры обслуживания прерывания beep_isr есть важная проблема! Вы не можете просто использовать регистр AX и не сохранить его. Решение простое, если вы написали: cmp WORD [cs:sound_counter], 0 je _bi_stop dec WORD [cs:sound_counter]. Нет необходимости использовать AX вообще. - person Sep Roland; 21.05.2017
comment
Большое спасибо @SepRoland! Я обновляю ответ с кредитами :) - person Margaret Bloom; 21.05.2017