Programowanie glosniczka w assemblerze
Autor: Bogdan Drozdowski, bogdandr (at) op.pl
Czy nie myslicie czasem, jakby to bylo, gdyby mozna bylo wzbogacic swoj
program oprocz efektu wizualnego, takze o efekt dzwiekowy?
Programowanie kart dzwiekowych (zwlaszcza tych nowoczesnych) moze sprawiac
niemale klopoty. Stary, poczciwy PC-Speaker jest jednak urzadzeniem wzglednie
prostym w programowaniu.
I to wlasnie tutaj udowodnie. Najpierw troszke teorii, potem - do dziela!
Sporo urzadzen w komputerze ma wlasne porty, przez ktore mozna sie z nimi
komunikowac. Jednak glosniczek komputerowy nie ma wlasnego portu.
Jest tak przede wszystkim ze wzgledu na oszczednosci w budowie pierwszych
PC-tow. Zamiast dac osobny port na glosnik, firmy produkujace komputery
wcisnely go "pod opieke" dwoch innych urzadzen:
- czasomierza systemowego, ktory posluzy nam do wytworzenia impulsow
odpowiedniej czestotliwosci
- kontrolera klawiatury, ktory kontroluje, czy jest otwarty "kanal" z
czasomierza do glosniczka, tzn. czy mozna bedzie wysylac informacje.
Podstawowe porty czasomierza to porty od 40h do 43h (caly zakres to
40h - 5fh, h = szestnastkowo), kontrolera klawiatury zas - 60h do 64h
(caly zakres: 60h - 6fh).
Nie bedziemy ich jednak wszystkich uzywac. Beda na interesowac tylko porty
42h, 43h i 61h.
Zacznijmy wiec cos pisac:
in al,61h
or al,3
out 61h,al
Co zrobilismy? W spisie portow (Listy Przerwan 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 biezacy status kontrolera, "or al,3" ustawia
(wlacza) bity 0 (wlaczenie bramki do glosniczka) oraz 1 (wlaczenie
mozliwosci wysylania danych do glosniczka), "out 61h,al" zapisuje nowy
status do kontrolera.
Glosniczek jest wlaczony. Trzeba mu podac jakis sygnal. Do tego posluzy
nam czasomierz. W spisie portow 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 portow tych nie bedziemy wysylac jednak czestotliwosci, ktora chcemy
uzyskac. Czasomierz pracuje na czestotliwosci 1193181 (1234DDh) Hz i
to te wartosc dzielimy przez zadana czestotliwosc. Wynik wysylamy do
odpowiednich portow.
Piszmy wiec:
mov bx,440h ; Standardowe "A", 440 Hz
mov dx,12h ; gorna czesc "1234dd"
mov ax,34ddh ; dolna czesc "1234dd"
div bx ; ax = wartosc do wyslania
push ax
mov al,0b6h
out 43h,al
pop ax
out 42h,al
mov al,ah
out 42,al
No i co my tutaj znowu zrobilismy?
4 pierwsze komendy to oczywiscie uzyskanie wartosci do wyslania na port,
ale reszta?
Najpierw: 0b6h = 1011 0110
===========================
Bity 7 i 6 = 10 = wybierz (standardowo niezajety) czasomierz nr 2 (lacznie
sa 3: zegar czasu rzeczywistego, czasomierz odswiezania pamieci RAM i ten
trzeci, nieuzywany)
Bity 5 i 4 = 11 = zapisujemy do czasomierza najpierw mlodsze bity (0-7)
wartosci, 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 wysylamy mlodszy bajt, AL a potem starszy, AH.
Skoro na port mozna wyslac najwieksza wartosc 0ffffh (teoretycznie najwieksza
jest 10000h, obcinana do 0000h), to jakiej odpowiada to czestotliwosci?
1234dd / 10000h to ok. 12h, czyli 18. A dokladniej jest to cos okolo 18,2 Hz -
standardowa czestotliwosc zegara w komputerze (tj. aby odmierzyc 1 sekunde
trzeba ok 18 tykniec tego zegara)
Nasz glosniczek juz gra. Teraz trzeba sprawic, bo to troszke potrwalo.
Pomocne bedzie przerwanie 15h, funkcja 86h:
mov cx,0fh
mov dx,4240h
mov ah,86h
int 15h ; pauza o dlugosci CX:DX mikrosekund
I dzwiek trwa 1 sekunde (F4240h = 1.000.000). Teraz trzeba go wylaczyc.
Nic prostszego. Po prostu zamkniemy przejscie miedzy czasomierzem a
glosniczkiem:
in al,61h
and al,not 3 ; zerujemy bity 0 i 1
; NASM: "and al,~3"
out 61h,al
Mam nadzieje, ze podalem wystarczajaco informacji, abyscie samodzielnie
zaczeli programowac glosniczek. Jesli mi sie nie udalo, to zawsze mozecie
skorzystac z gotowej procedury z mojej biblioteki.
To juz koniec. Milej zabawy!