Programming the PC speaker in assembly under Linux

Do sometimes think about how would it be if your programs had not only visual effects, but also some sound?

Programming the sound cards (especially the modern ones) can be quite troublesome. Fortunately, the good old PC-speaker is a device which you can program quite easily. This is what I'm going to prove here. First some theory.

Linux is an operating system which works fully in protected mode. This is why we can't use the interesting (42h, 43h, 61h) input/output ports directly without root privileges.

Fortunately, the system function sys_ioctl (number 54) will help us in bringing the PC-speaker alive. This function expects the following parameters:

But this is not all. We'd like our sound to last for a while. For that we'll use the system function sys_nanosleep (number 162). Its syntax is simple:

As you can see, the algorithm of our program is simple:

  1. Open /dev/console for writing. In case of failure use STDOUT
  2. Maybe call sys_ioctl with EDX=0 to turn off the currently played sound
  3. As many times as it will be needed, call sys_ioctl with the correct values in EDX and call sys_nanosleep
  4. Call sys_ioctl with EDX=0 to turn off the sound
  5. If we opened /dev/console, close it

Example program follows (using my library isn't necessary - the comments tell what to change):

(skip the code)
; A program making sounds from the PC-speaker using sys_ioctl
; Author: Bogdan D.
; Contact: bogdandr (at) op (dot) pl
; assemble:
;   fasm spkr.asm spkr

format ELF executable
entry _start

segment readable executable

include "bibl/incl/linuxbsd/fasm/"


	mov	eax, sys_open	; sys_open = 5
	mov	ebx, konsola
	mov	ecx, O_WRONLY	; O_WRONLY = 1
	mov	edx, 777o
	int	80h

	cmp	eax, 0		; was there an error (EAX < 0) ?
	jg	.otw_ok

	mov	eax, 1		;if we didn't open the console, use STDOUT (1)
	mov	ebx, eax	; EBX = file handle

	mov	eax, sys_ioctl	; sys_ioctl = 54
	mov	ecx, KIOCSOUND
	xor	edx, edx	; turning off the current sound
	int	80h

	mov	eax, sys_ioctl
	mov	edx, 2711	; 2711 = 1234DDh/440. 440 Hz is the 'A' note
	int	80h

        mov     cx, 0fh
        mov     dx, 4240h	; 0F4240h is 1 decimal million in hexadecimal
        call    pauza

        mov     eax, sys_ioctl
	mov	ecx, KIOCSOUND
	xor	edx, edx	; turning off the sound
	int     80h

	cmp	ebx, 2		; check if using /dev/console or STDOUT
	jbe	.koniec

	mov	eax, sys_close	; sys_close = 6
	int	80h		; close the open console file

	mov	eax, 1
	xor	ebx, ebx
	int	80h

pauza:				;procedure pausing for CX:DX milliseconds
	push    ebx
	push    ecx
	push    edx

	mov     ax, cx
	shl     eax, 16
	mov     ebx, 1000000
	mov     ax, dx 			; EAX = CX:DX
	xor     edx, edx
	div     ebx			; divide CX:DX by 1 million
	mov     [t1.tv_sec], eax 	; EAX = number of seconds

	mov     ebx, 1000
	mov     eax, edx		;EAX = number of microseconds left
	mul     ebx
	mov     [t1.tv_nsec], eax 	; EAX = number of nanoseconds

	mov     eax, sys_nanosleep	; function number 162
	mov     ebx, t1
	mov     ecx, t2
	int     80h

	pop     edx
	pop     ecx
	pop     ebx


segment readable writeable

konsola		db	"/dev/console", 0

struc	timespec
	.tv_sec		rd 1
	.tv_nsec	rd 1

t1 timespec
t2 timespec

I hope to have given enough information for you to start programming the speaker on your own. If the program isn't working, you may need to compile pc-speaker support into your kernel (or insert the needed modules). Sometimes root privileges are necessary.

Have fun!

On-line contents (Alt+2)
Helpers for people with disabilities (Alt+0)