Reputation: 21
I am working on a "simon" game in assembly I need to make a beep sound whenever a button turns on, the beeps should be different from each other as well. thanks
Upvotes: 2
Views: 6300
Reputation: 44146
You can use the speaker to keep your design simple.
The speaker lets you play square waves at different frequencies, it can actually be used to reproduce digital audio but that's more involved.
The speaker is just an electromagnet, when the current flows through it, it is pulled back otherwise it stays in its default position.
By moving the speaker back and forth it's possible to create sound waves.
The speaker can be moved manually or by using the PIT's channel 2.
Bit 0 of port 61h controls the speaker source (0 = manual, 1 = PIT) and the bit 1 of the same port is the "speaker enable" bit when using the PIT (the speaker "position" when not).
Here's a schematic (from this page) missing the manual driving part:
The PIT is controlled via port 40h-43h, we will use Mode 3 (Square Wave generator) setting each time both bytes of the divider.
The PIT has an oscillator running at about 1.193180 MHz, the divider is used to control the period of the square wave.
Without dealing with internals: at each tick of the PIT oscillator the divider loaded is decremented. The period of the square wave is equal to the time needed by the PIT to decrement the divider down to zero.
Producing a sound is just a matter of programming the PIT with the desired divider and enabling the speaker.
At some time later, we need to disable it.
An easy way to do this is using the int 1ch that is called 18.2 times a second.
By saving a duration in a variable when first playing a sound, by decrementing it at each tick of the int 1ch and by disabling the speaker when the count reaches zero it's possible to control the duration of the beep.
Using the int 1ch requires a setup function (beep_setup
) and a teardown function (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
Special thanks to Sep Roland for fixing a flaw in the original code!
You can use beep_play
to play a beep, the units used are the "natural" unit of the hardware configuration explained above.
If your frequencies and duration are fixed, these units simplify the code at no cost.
The beep stops after the duration given, you can use beep_stop
to stop it forcefully.
Playing multiple sound at a time it's impossible (even mixing them is impossible without resorting to PWM techniques).
Calling beep_play
while another beep is in play will have the effect of stopping the current beep and starting the new one.
Upvotes: 10