Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playing sound with the PC Speaker in x86 Assembly

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
like image 267
Insert joke here Avatar asked Jan 16 '20 13:01

Insert joke here


People also ask

How do I play audio from my device to my speakers?

On the "Input from:" section, choose "Built-in Input: Line In". Set the "Output to:" to "Default System Output". "Built-in Output: Internal Speakers" should work as well. After you have those set, click "Pass Thru", play some audio from your device, and listen to it playing through your speakers!

Is there a way to play standard audio through the CPU?

To play standard audio, the CPU needs to be interrupted 44100 times every second. The easiest way to do this is to (re)program timer zero (0) to interrupt you at 44100Hz. You also need a way to actually change the speaker's position. This could be done simply through bit 1 of port 0x61 but this option is too slow to be practical.

What is a PC speaker?

The PC Speaker is the most primitive sound device available on PC compatible systems. It is characterized by the distinctive "beeps" and "squeaks" that it can be made to produce and is therefore sometimes referred to as the "PC Beeper" or the "PC Squeaker". The speaker itself has two possible positions, "in" and "out".

How to play standard audio on the pit?

You should first have the PIT interrupt you each time the speaker's position needs to be changed. To play standard audio, the CPU needs to be interrupted 44100 times every second. The easiest way to do this is to (re)program timer zero (0) to interrupt you at 44100Hz. You also need a way to actually change the speaker's position.


1 Answers

I'm kind of late to the party and probably you may have figured it out, but here's my answer for the future StackOverflow 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=0, DX=F4240h will play the
; specified tone for one second.
; 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) Immidiately interrupt. The delay is already in CX:DX.
    IN AL, 61h          ; 5) Read the current keyboard controller status.
    AND AL, 0FCh        ; 5) Zero 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.
like image 157
Kamila Szewczyk Avatar answered Oct 10 '22 22:10

Kamila Szewczyk