   #Start Contents English version

             Programowanie glosniczka w asemblerze pod Linuksem

   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!

   Linux jest systemem dzialajacym w pelni w trybie chronionym. Dlatego
   bez uprawnien administratora nie mozemy bezposrednio pisac do
   interesujacych nas portow (42h, 43h, 61h).

   Na szczescie istnieje funkcja systemowa sys_ioctl (numer 54) i to ona
   nam pomoze w ozywieniu glosniczka systemowego. Funkcja ta jako
   parametry przyjmuje:
     * EBX = deskryptor otwartego pliku (dla nas bedzie to /dev/console
       lub standardowe wyjscie - STDOUT) do zapisu
     * ECX = stala KIOCSOUND = 0x4B2F (patrz: /usr/include/linux/kd.h)
     * EDX =
          + 0 gdy chcemy wylaczyc dzwiek
          + 1234DDh / czestotliwosc, gdy chcemy miec dzwiek o zadanej
            czestotliwosci

   Ale to nie wszystko. Chcemy, by nasz dzwiek chwile potrwal. W tym celu
   skorzystamy z funkcji sys_nanosleep (numer 162). Jej skladnia jest
   prosta:
     * EBX = adres struktury timespec wygladajacej tak (w skladni FASM):
        struc   timespec
         {
                .tv_sec         rd 1
                .tv_nsec        rd 1
         }
       i zawierajacej wpisane ilosci sekund i nanosekund, ktore nalezy
       odczekac.
     * ECX = adres struktury timespec, do ktorej funkcja zapisze wynik
       swojego dzialania.

   Jak widac schemat dzialania naszego programu jest dosc prosty:
    1. Otworzyc /dev/console do zapisu. W przypadku niepowodzenia uzyc
       STDOUT
    2. Ewentualnie wywolac sys_ioctl z EDX=0 w celu wylaczenia aktualnie
       trwajacego dzwieku
    3. Tyle razy ile trzeba, wywolac sys_ioctl z odpowiednimi wartosciami
       w EDX i korzystac z funkcji sys_nanosleep
    4. Wywolac sys_ioctl z EDX=0 w celu wylaczenia dzwieku
    5. Jesli otworzylismy /dev/console, zamknac ten deskryptor

   Przykladowy program wyglada tak (uzywanie zalacznikow z mojej
   biblioteki nie jest konieczne - w kodzie mowie, jak i co zamienic):
   (przeskocz program)
; Program wytwarzajacy dzwieki z glosniczka 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 wystapil blad (EAX < 0) ?
        jg      .otw_ok

        mov     eax, 1          ; jak nie otworzylismy 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        ; wylaczenie ewentualnych dzwiekow
        int     80h

        mov     eax, sys_ioctl
        mov     edx, 2711       ; 2711 = 1234DDh/440. 440 Hz to dzwiek A
        int     80h

        mov     cx, 0fh
        mov     dx, 4240h       ; 0F4240h to 1 milion dziesietnie
        call    pauza

        mov     eax, sys_ioctl
        mov     ecx, KIOCSOUND
        xor     edx, edx        ; wylaczamy dzwiek
        int     80h

        cmp     ebx, 2          ; sprawdzamy, czy uzywamy /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 pauzujaca 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 = pozostala 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 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.

   Jesli program nie powoduje wydawania zadnych dzwiekow, moze trzeba
   wkompilowac obsluge glosniczka do jadra (lub zaladowac odpowiedni
   modul). Czasem moga byc potrzebne uprawnienia administratora.

   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)
