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ñ:
kana³z czasomierza do g³o¶niczka, czyli czy mo¿na bêdzie wysy³aæ informacje.
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:
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:
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:
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:
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!