Programowanie g³o¶niczka w asemblerze

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!

Sporo urz±dzeñ w komputerze ma w³asne porty, przez które mo¿na siê z nimi komunikowaæ. Jednak g³o¶niczek komputerowy nie ma w³asnego portu.
Jest tak przede wszystkim ze wzglêdu na oszczêdno¶ci w budowie pierwszych PC-tów. Zamiast daæ osobny port na g³o¶nik, firmy produkuj±ce komputery wcisnê³y go pod opiekê dwóch innych urz±dzeñ:

Podstawowe porty czasomierza to porty od 40h do 43h (ca³y zakres to 40h - 5fh, h oznacza szesnastkowo), kontrolera klawiatury za¶ - 60h do 64h (ca³y zakres: 60h - 6fh).
Nie bêdziemy ich jednak wszystkich u¿ywaæ. Bêd± na interesowaæ tylko porty 42h, 43h i 61h.

Zacznijmy wiêc co¶ pisaæ:

	in al,61h
	or al,3
	out 61h,al

Co zrobili¶my? W spisie portów Listy Przerwañ Ralfa Brown'a czytamy:


(przeskocz port 61h)
	0061  R-  KB controller port B control register (ISA, EISA)
	0061  -W  KB controller port B (ISA, EISA)

	(R - czytanie (read) , W - pisanie (write))

oraz:


(przeskocz opis portu 61h)
	Bitfields for KB controller port B (system control port) [output]:
	Bit(s)  Description     (Table P0392)
	 7      pulse to 1 for IRQ1 reset (PC,XT)
	 6-4    reserved
	 3      I/O channel parity check disable
	 2      RAM parity check disable
	 1      speaker data enable
	 0      timer 2 gate to speaker enable

Komenda IN AL,61h czyta bie¿±cy status kontrolera, OR AL,3 ustawia (w³±cza) bity 0 (w³±czenie bramki do g³o¶niczka) oraz 1 (w³±czenie mo¿liwo¶ci wysy³ania danych do g³o¶niczka), OUT 61h,AL zapisuje nowy status do kontrolera.

G³o¶niczek jest w³±czony. Trzeba mu podaæ jaki¶ sygna³. Do tego pos³u¿y nam czasomierz. W spisie portów czytamy:


(przeskocz opis portów 42h i 43h)
	0042  RW  PIT  counter 2, cassette & speaker

	0043  RW  PIT  mode port, control word register for counters 0-2
	    Once a control word has been written (43h), it must be followed
	    immediately by performing the corresponding action to the counter
	    registers (40h-42h), else the system may hang!!

Do portów tych nie bêdziemy wysy³aæ jednak czêstotliwo¶ci, któr± chcemy uzyskaæ. Czasomierz pracuje na czêstotliwo¶ci 1193181 (1234DDh) Hz i to tê warto¶æ dzielimy przez ¿±dan± czêstotliwo¶æ, a wynik wysy³amy do odpowiednich portów.

Piszmy wiêc:


(przeskocz w³±czanie g³o¶niczka)
	mov bx,440h	; Standardowy d¼wiêk A, 440 Hz
	mov dx,12h	; górna czê¶æ liczby 1234dd
	mov ax,34ddh	; dolna czê¶æ liczby 1234dd
	div bx		; ax = warto¶æ do wys³ania

	pushf		; zachowaj flagi
	push ax		; zachowaj warto¶æ do wys³ania
	cli		; wy³±cz przerwania
	mov al,0b6h
	out 43h,al	; wy¶lij komendê

	pop ax
	out 42h,al	; wy¶lij pierwsz± po³owê licznika
	mov al,ah
	out 42h,al	; wy¶lij drug± po³owê licznika
	popf		; przywróæ stan flagi przerwañ
	

No i co my tutaj znowu zrobili¶my?
4 pierwsze komendy to oczywi¶cie uzyskanie warto¶ci do wys³ania na port, ale reszta?

Najpierw: 0b6h = 1011 0110

Bity 7 i 6 = 10 = wybierz (standardowo niezajêty) czasomierz nr 2 (³±cznie s± 3: zegar czasu rzeczywistego, czasomierz od¶wie¿ania pamiêci RAM i ten trzeci, nieu¿ywany)
Bity 5 i 4 = 11 = zapisujemy do czasomierza najpierw m³odsze bity (0-7) warto¶ci, potem starsze (8-15)
Bity 3-1 = 011 = wybierz tryb nr 3, czyli generator fali kwadratowej
Bit 0 = 0 = licznik binarny 16-bitowy.

Zgodnie z tym, najpierw wysy³amy m³odszy bajt, AL a potem starszy, AH.

Skoro na port mo¿na wys³aæ najwiêksz± warto¶æ 0ffffh (teoretycznie najwiêksza jest 10000h, obcinana do 0000h), to jakiej odpowiada to czêstotliwo¶ci?
1234dd / 10000h to ok. 12h, czyli 18. A dok³adniej jest to co¶ oko³o 18,2 Hz - standardowa czêstotliwo¶æ zegara w komputerze (aby odmierzyæ 1 sekundê trzeba ok 18 tykniêæ tego zegara)

Nasz g³o¶niczek ju¿ gra. Teraz trzeba sprawiæ, bo to troszkê potrwa³o. Pomocne bêdzie przerwanie 15h, funkcja 86h:

	mov cx,0fh
	mov dx,4240h
	mov ah,86h
	int 15h			; pauza o d³ugo¶ci CX:DX mikrosekund

I d¼wiêk trwa 1 sekundê (F4240h = 1.000.000). Teraz trzeba go wy³±czyæ. Nic prostszego. Po prostu zamkniemy przej¶cie miêdzy czasomierzem a g³o¶niczkiem:

	in al,61h
	and al,not 3		; zerujemy bity 0 i 1
				; NASM:  "and al,~3"
	out 61h,al

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.

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)