2017-05-16 41 views
1

我正在组装一个“simon”游戏,每当按钮打开时,我都需要发出嘟嘟声,哔哔声也应该彼此不同。 谢谢装配8086 - DOSBOX - 如何产生哔声?

+1

[在DOSBox的Sound Blaster设备上播放.wav文件]可能的重复(http://stackoverflow.com/questions/41359112/playing-wav-files-on-dosboxs-sound-blaster-device) –

+1

您是否咨询过一个DOS中断引用? – Michael

回答

6

您可以使用speaker保持您的设计简单。
扬声器可让您以不同的频率播放方波,​​但这更涉及。

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

扬声器可以手动或通过使用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播放一个嘟嘟声,使用的单位是上述硬件配置的“自然”单位。
如果您的频率和持续时间是固定的,这些设备将免费简化代码。

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

一次播放多个声音是不可能的(甚至在不采用PWM技术的情况下混合它们也是不可能的)。
呼叫beep_play而另一声嘟嘟声将起到停止当前嘟嘟声并启动新蜂鸣器的作用。

+0

一个很好的答案。 +1但是* beep_isr *中断服务程序存在一个重要问题!你不能只使用'AX'寄存器,而不能保存它。如果你写了:'cmp WORD [cs:sound_counter],0''je_bi_stop''dec WORD [cs:sound_counter]'',解决方法很简单。根本不需要使用“AX”。 –

+0

非常感谢@SepRoland!我正在更新信用的答案:) –