   #Start Contents

                    Programowanie glosniczka w asemblerze

   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, czyli czy mozna bedzie wysylac
       informacje.

   Podstawowe porty czasomierza to porty od 40h do 43h (caly zakres to
   40h - 5fh, "h" oznacza "szesnastkowo"), 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:
   (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 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:
   (przeskocz opis portow 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 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, a wynik
   wysylamy do odpowiednich portow.

   Piszmy wiec:
   (przeskocz wlaczanie glosniczka)
        mov bx,440h     ; Standardowy dzwiek A, 440 Hz
        mov dx,12h      ; gorna czesc liczby 1234dd
        mov ax,34ddh    ; dolna czesc liczby 1234dd
        div bx          ; ax = wartosc do wyslania

        pushf           ; zachowaj flagi
        push ax         ; zachowaj wartosc do wyslania
        cli             ; wylacz przerwania
        mov al,0b6h
        out 43h,al      ; wyslij komende

        pop ax
        out 42h,al      ; wyslij pierwsza polowe licznika
        mov al,ah
        out 42h,al      ; wyslij druga polowe licznika
        popf            ; przywroc stan flagi przerwan

   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 (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!

   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
