So I'm trying to play a single tone using the pc speaker in x86 assembly, It play a sound, but when I try to turn it off again... the tone begins do what I can only describe as shaking.
Also I'm making this for a 16 bit OS if that means anything.
Here's my sound.asm file
; ------------------------------------------------------------------
; os_play_sound -- Play a single tone using the pc speaker
; IN: CX = tone, BX = duration
os_play_sound:
mov al, 182
out 0x43, al
mov ax, cx
out 0x42, al
mov al, ah
out 0x42, al
in al, 0x61
or al, 00000011b
out 0x61, al
.pause1:
mov cx, 65535
.pause2:
dec cx
jne .pause2
dec bx
jne .pause1
in al, 0x61
and al, 11111100b
out 0x61, al
ret
And here's the part in main.asm where I'm calling the sound.asm label from
mov cx, 9121
mov bx, 25
call os_play_sound
I'm kind of late to the party and probably you may have figured it out, but here's my answer for the future Stack Overflow lurking generations ;).
You can test the following snippet (based on your code; slightly cleaned up) that will in fact issue the tone, but wait using INT15H
function AX=86H
. It's usually a better practice than busy wait, but slightly worse than reprogramming the PIT for your needs. As you're using MikeOS though as your main codebase, I wanted to keep the code as simple as possible. Comments are included for further understanding of the code.
; ---------------------------------------------
; Generate tone with frequency specified in AX.
; The tone will last for CX:DX microseconds.
; For instance, CX=000Fh, DX=4240h will play the
; specified tone for one second (CX:DX = 1000000).
; Registers preserved.
tone:
PUSHA ; Prolog: Preserve all registers
MOV BX, AX ; 1) Preserve the note value by storing it in BX.
MOV AL, 182 ; 2) Set up the write to the control word register.
OUT 43h, AL ; 2) Perform the write.
MOV AX, BX ; 2) Pull back the frequency from BX.
OUT 42h, AL ; 2) Send lower byte of the frequency.
MOV AL, AH ; 2) Load higher byte of the frequency.
OUT 42h, AL ; 2) Send the higher byte.
IN AL, 61h ; 3) Read the current keyboard controller status.
OR AL, 03h ; 3) Turn on 0 and 1 bit, enabling the PC speaker gate and the data transfer.
OUT 61h, AL ; 3) Save the new keyboard controller status.
MOV AH, 86h ; 4) Load the BIOS WAIT, int15h function AH=86h.
INT 15h ; 4) Immediately interrupt. The delay is already in CX:DX.
IN AL, 61h ; 5) Read the current keyboard controller status.
AND AL, 0FCh ; 5) Turn off 0 and 1 bit, simply disabling the gate.
OUT 61h, AL ; 5) Write the new keyboard controller status.
POPA ; Epilog: Pop off all the registers pushed
RET ; Epilog: Return.