Programowanie g³o¶niczka w asemblerze pod Linuksem

Czy nie my¶licie czasem, jakby to by³o, gdyby mo¿na by³o wzbogaciæ swój program oprócz efektu wizualnego, tak¿e o efekt d¼wiêkowy?

Programowanie kart d¼wiêkowych (zw³aszcza tych nowoczesnych) mo¿e sprawiaæ niema³e k³opoty. Stary, poczciwy PC-Speaker jest jednak urz±dzeniem wzglêdnie prostym w programowaniu i to w³a¶nie tutaj udowodniê. Najpierw troszkê teorii, potem - do dzie³a!

Linux jest systemem dzia³aj±cym w pe³ni w trybie chronionym. Dlatego bez uprawnieñ administratora nie mo¿emy bezpo¶rednio pisaæ do interesuj±cych nas portów (42h, 43h, 61h).

Na szczê¶cie istnieje funkcja systemowa sys_ioctl (numer 54) i to ona nam pomo¿e w o¿ywieniu g³o¶niczka systemowego. Funkcja ta jako parametry przyjmuje:

Ale to nie wszystko. Chcemy, by nasz d¼wiêk chwilê potrwa³. W tym celu skorzystamy z funkcji sys_nanosleep (numer 162). Jej sk³adnia jest prosta:

Jak widaæ schemat dzia³ania naszego programu jest do¶æ prosty:

  1. Otworzyæ /dev/console do zapisu. W przypadku niepowodzenia u¿yæ STDOUT
  2. Ewentualnie wywo³aæ sys_ioctl z EDX=0 w celu wy³±czenia aktualnie trwaj±cego d¼wiêku
  3. Tyle razy ile trzeba, wywo³aæ sys_ioctl z odpowiednimi warto¶ciami w EDX i korzystaæ z funkcji sys_nanosleep
  4. Wywo³aæ sys_ioctl z EDX=0 w celu wy³±czenia d¼wiêku
  5. Je¶li otworzyli¶my /dev/console, zamkn±æ ten deskryptor

Przyk³adowy program wygl±da tak (u¿ywanie za³±czników z mojej biblioteki nie jest konieczne - w kodzie mówiê, jak i co zamieniæ):


(przeskocz program)
; Program wytwarzaj±cy d¼wiêki z g³o¶niczka przez sys_ioctl
; Autor: Bogdan D.
; Kontakt: bogdandr (at) op (dot) pl
;
; kompilacja:
;   fasm spkr.asm spkr

format ELF executable
entry _start

segment readable executable

include "bibl/incl/linuxbsd/fasm/fasm_system.inc"

KIOCSOUND	= 0x4B2F

_start:
	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		; czy wyst±pi³ b³±d (EAX < 0) ?
	jg	.otw_ok

	mov	eax, 1		; jak nie otworzyli¶my konsoli, piszemy
				; na STDOUT (1)
.otw_ok:
	mov	ebx, eax	; EBX = uchwyt do pliku

	mov	eax, sys_ioctl	; sys_ioctl = 54
	mov	ecx, KIOCSOUND
	xor	edx, edx	; wy³±czenie ewentualnych d¼wiêków
	int	80h

	mov	eax, sys_ioctl
	mov	edx, 2711	; 2711 = 1234DDh/440. 440 Hz to d¼wiêk A
	int	80h

        mov     cx, 0fh
        mov     dx, 4240h	; 0F4240h to 1 milion dziesiêtnie
        call    pauza

        mov     eax, sys_ioctl
	mov	ecx, KIOCSOUND
	xor	edx, edx	; wy³±czamy d¼wiêk
	int     80h

	cmp	ebx, 2		; sprawdzamy, czy u¿ywamy /dev/console
				; czy STDOUT
	jbe	.koniec

	mov	eax, sys_close	; sys_close = 6
	int	80h		; zamykamy otwarty plik konsoli

.koniec:
	mov	eax, 1
	xor	ebx, ebx
	int	80h

pauza:				;procedura pauzuj±ca przez CX:DX milisekund
	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			; CX:DX dzielimy przez milion
	mov     [t1.tv_sec], eax 	; EAX = liczba sekund

	mov     ebx, 1000
	mov     eax, edx		;EAX = pozosta³a liczba mikrosekund
	mul     ebx
	mov     [t1.tv_nsec], eax 	; EAX = liczba nanosekund

	mov     eax, sys_nanosleep	; funkcja numer 162
	mov     ebx, t1
	mov     ecx, t2
	int     80h

	pop     edx
	pop     ecx
	pop     ebx

	ret

segment readable writeable

konsola		db	"/dev/console", 0

struc	timespec
 {
	.tv_sec		rd 1
	.tv_nsec	rd 1
 }

t1 timespec
t2 timespec

Mam nadziejê, ¿e poda³em wystarczaj±co informacji, aby¶cie samodzielnie zaczêli programowaæ g³o¶niczek. Je¶li mi siê nie uda³o, to zawsze mo¿ecie skorzystaæ z gotowej procedury z mojej biblioteki.

Je¶li program nie powoduje wydawania ¿adnych d¼wiêków, mo¿e trzeba wkompilowaæ obs³ugê g³o¶niczka do j±dra (lub za³adowaæ odpowiedni modu³). Czasem mog± byæ potrzebne uprawnienia administratora.

To ju¿ koniec. Mi³ej zabawy!


Spis tre¶ci off-line (klawisz dostêpu 1)
Spis tre¶ci on-line (klawisz dostêpu 2)
U³atwienia dla niepe³nosprawnych (klawisz dostêpu 0)