2017-05-16 4 views
1

Je travaille sur un jeu "simon" en montage. J'ai besoin de faire un bip à chaque fois qu'un bouton s'allume, les bips doivent être différents les uns des autres. merciAssembly 8086 - DOSBOX - Comment produire un bip sonore?

+1

double possible de [Lecture de fichiers .wav sur le périphérique Sound Blaster de DOSBox] (http://stackoverflow.com/questions/41359112/playing-wav-files-on-dosboxs-sound-blaster-device) –

+1

Avez-vous consulté une référence d'interruption DOS? – Michael

Répondre

6

Vous pouvez utiliser le speaker pour garder votre design simple.
Le haut-parleur vous permet de jouer des ondes carrées à différentes fréquences, it can actually be used to reproduce digital audio mais c'est plus impliqué.

Le haut-parleur est juste un électroaimant, quand le courant le traverse, il est tiré vers l'arrière sinon il reste dans sa position par défaut.
En déplaçant l'enceinte d'avant en arrière, il est possible de créer des ondes sonores.

Le haut-parleur peut être déplacé manuellement ou en utilisant le canal de » PIT 2.
Bit 0 de l'orifice 61h commande la source de haut-parleur (0 = manuel, 1 = PIT) et le bit 1 du même port est le bit "speaker enable" lors de l'utilisation du PIT (la "position" du locuteur quand il ne l'est pas).

Voici un schéma (de ce page) manque la partie manuelle de conduite:

Speaker schematic

Le PIT est contrôlé via le port 40h-43h, nous allons utiliser le mode 3 (Square générateur Wave) réglage chaque fois les deux octets du diviseur.
Le PIT a un oscillateur fonctionnant à environ 1.193180 MHz, le diviseur est utilisé pour contrôler la période de l'onde carrée.
Sans traiter les internes: à chaque coche de l'oscillateur PIT, le diviseur chargé est décrémenté. La période de l'onde carrée est égale au temps nécessaire au PIT pour décrémenter le diviseur jusqu'à zéro.

Produire un son consiste simplement à programmer le PIT avec le diviseur souhaité et à activer le haut-parleur.
Plus tard, nous devrons le désactiver.
Un moyen facile de faire cela est d'utiliser le int 1ch qui est appelé 18,2 fois par seconde. En enregistrant une durée dans une variable lors de la première lecture d'un son, en la décrémentant à chaque tick de l'int 1ch et en désactivant l'enceinte lorsque le compte atteint zéro, il est possible de contrôler la durée du bip. L'utilisation de int 1ch nécessite une fonction de configuration (beep_setup) et une fonction de démontage (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 

grâce à Sep Roland spéciaux pour la fixation d'une faille dans le code original!

Vous pouvez utiliser beep_play pour émettre un bip, les unités utilisées sont l'unité «naturelle» de la configuration matérielle expliquée ci-dessus.
Si vos fréquences et durées sont fixes, ces unités simplifient le code sans frais.

Le bip s'arrête après la durée indiquée, vous pouvez utiliser beep_stop pour l'arrêter avec force.

Jouer plusieurs sons à la fois c'est impossible (même les mélanger est impossible sans recourir aux techniques PWM).
L'appel du beep_play alors qu'un autre bip est en cours aura pour effet d'arrêter le bip actuel et de démarrer le nouveau.

+0

Une excellente réponse. +1 Mais la routine de service d'interruption * beep_isr * a un problème important! Vous ne pouvez pas simplement utiliser le registre 'AX' et ne pas le conserver. La solution est simple si vous avez écrit: 'cmp WORD [cs: sound_counter], 0' 'je _bi_stop'' dec WORD [cs: sound_counter] '. Pas besoin d'utiliser 'AX' du tout. –

+0

Merci beaucoup @SepRoland! Je mets à jour la réponse avec des crédits :) –