程序集8086 - DOSBOX - 如何产生哔声?

时间:2017-05-16 08:58:37

标签: assembly x86 dos x86-16 dosbox

我正在制作" simon"组装中的游戏我需要在按钮打开时发出哔哔声,哔哔声也应该彼此不同。 感谢

1 个答案:

答案 0 :(得分:7)

您可以使用speaker来简化您的设计 扬声器让您以不同的频率播放方波,it can actually be used to reproduce digital audio但更多参与其中。

扬声器只是一个电磁铁,当电流流过它时,它会被拉回,否则它会保持在默认位置。
通过前后移动扬声器,可以产生声波。

可以手动或使用PIT频道2移动扬声器 端口61h的位0控制扬声器源(0 =手动,1 = PIT),同一端口的位1是"扬声器使能"使用PIT时的位(扬声器"位置"否则)。

这是一个示意图(来自此page),错过了手动驾驶部分:

Speaker schematic

PIT通过端口40h-43h控制,我们将每次使用模式3(方波发生器)设置分频器的两个字节。
PIT的振荡器运行频率约为1.193180 MHz,分频器用于控制方波的周期。
不处理内部结构:在PIT振荡器的每个刻度处,加载的分频器递减。方波的周期等于PIT将分频器减小到零所需的时间。

产生声音只是用所需的分频器编程PIT并启用扬声器 稍后,我们需要禁用它 一种简单的方法是使用每秒18.2次调用的int 1ch

通过在第一次播放声音时在变量中保存持续时间,通过在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

特别感谢Sep Roland修复原始代码中的漏洞!

您可以使用beep_play发出哔哔声,使用的单位是" natural"上面说明的硬件配置的单位 如果您的频率和持续时间是固定的,这些单位可以免费简化代码。

在给定的持续时间后,蜂鸣声停止,您可以使用beep_stop强制停止。

一次播放多个声音是不可能的(如果不采用PWM技术,甚至无法混音)。 在发出另一声嘟嘟声时调用beep_play会产生停止当前蜂鸣声并启动新蜂鸣声的效果。