DOS BIBL TUT


Pisanie wlasnych bibliotek w jezyku assembler
Autor: Bogdan Drozdowski, bogdandr (at) op.pl


Pewnie zdarzylo sie juz wam uslyszec o kims innym:
"Alez on(a) jest swietnym(a) programista(ka)! Nawet pisze wlasne biblioteki!"

Pokaze teraz, ze nie jest to trudne, nie wazne jak przerazajacym sie to moze
wydawac. Osoby, ktore przeczytaja ten artykul i zdobeda troszke wprawy beda
mogly mowic:
"Phi, a co to za filozofia pisac wlasne biblioteki!"


Zacznijmy wiec od pytania: co powinno sie znalezc w takiej bibliotece?
Moga to byc:

- Funkcje wejscia i wyjscia, podobnie jak np. w jezyku C
- Funkcje, ktore juz przepisywalismy ze 20 razy w roznych programach
- Sprawdzone funkcje, napisane przez kogos innego, ktorych nie umielibysmy
sami napisac, lub ktore "po prostu moga sie przydac"


Co to zas jest to owa "biblioteka"?
Jest to plik (najczesciej z rozszerzeniem .lib, teraz juz tez .dll), na
ktory sklada sie skompilowany kod, a wiec np. pliki .obj. Biblioteka
eksportuje na zewnatrz nazwy procedur w niej zawartych, aby linker wiedzial,
jaki adres podac programowi, ktory chce skorzystac z takiej procedury.

Bede w tym artykule uzywal skladni i linii polecen Turbo Assemblera (TASMa)
firmy Borland z linkerem TLink i bibliotekarzem TLib oraz NASMa
(Netwide Assembler) z linkerem ALink i darmowym bibliotekarzem
znalezionym w Internecie (patrz linki na dole strony).



Napiszmy wiec jakis prosty kod zrodlowy. Oto on:
================================
; wersja TASM

public _graj_dzwiek

biblioteka_dzwiek segment byte public 'bibl'
assume cs:biblioteka_dzwiek

;**************************************

_graj_dzwiek proc far



; wejscie: BX = zadana czestotliwosc dzwieku w Hz, co najmniej 19
; CX:DX = czas trwania dzwieku w mikrosekundach
;
; wyjscie: CF = 0 - wykonane bez bledow
; CF = 1 - blad: BX za maly



czasomierz equ 40h ;numer portu programowalnego czasomierza
klawiatura equ 60h ;numer portu kontrolera klawiatury

pushf ; zachowujemy modyfikowane rejestry
push ax
push dx
push si


cmp bx,19 ;najnizsza mozliwa czestotliwosc to ok. 18,2 Hz
jb _graj_blad


in al,klawiatura+1 ; port B kontrolera klawiatury
or al,3 ; ustawiamy bity: 0 i 1 - wlaczamy glosnik i
; bramke od licznika nr. 2 czasomierza
; do glosnika
out klawiatura+1,al



mov si,dx ;zachowujemy DX

mov dx,12h
mov ax,34ddh
div bx ;AX = 1193181 / czestotliwosc, DX=reszta

mov dl,al ;zachowujemy mlodszy bajt dzielnika
; czestotliwosci



mov al,0b6h

out czasomierz+3,al ;wysylamy komende:
; (bity 7-6) wybieramy licznik nr. 2,
; (bity 5-4) bedziemy pisac najpierw bity 0-7,
; potem bity 8-15
; (bity 3-1) tryb 3: generator fali kwadratowej
; (bit 0) licznik binarny 16-bitowy

mov al,dl ; odzyskujemy mlodszy bajt
out czasomierz+2,al ; port licznika nr. 2 i bity 0-7 dzielnika
; czestotliwosci
mov al,ah
out czasomierz+2,al ; bity 8-15

mov dx,si ;odzyskujemy DX


_graj_pauza:
mov ah,86h
int 15h ; pauza o dlugosci CX:DX mikrosekund

jnc _graj_juz
dec dx
sbb cx,0 ; w razie bledu zmniejszamy CX:DX
jmp short _graj_pauza

_graj_juz:

in al,klawiatura+1
and al,not 3 ; zerujemy bity: 0 i 1 - wylaczamy glosnik
; i bramke
out klawiatura+1,al

pop si ; przywracamy rejestry
pop dx
pop ax
popf
clc ; brak bledu

retf


_graj_blad:
pop si ; przywracamy rejestry
pop dx
pop ax
popf
stc ; blad

retf

_graj_dzwiek endp

;**************************************

biblioteka_dzwiek ends
end

==================================================================

Teraz ten sam kod w skladni NASMa:

==================================================================
; wersja NASM

global _graj_dzwiek

segment biblioteka_dzwiek

;**************************************

_graj_dzwiek:



; wejscie: BX = zadana czestotliwosc dzwieku w Hz, co najmniej 19
; CX:DX = czas trwania dzwieku w mikrosekundach
;
; wyjscie: CF = 0 - wykonane bez bledow
; CF = 1 - blad: BX za maly



czasomierz equ 40h ;numer portu programowalnego czasomierza
klawiatura equ 60h ;numer portu kontrolera klawiatury

pushf
push ax
push dx
push si


cmp bx,19 ;najnizsza mozliwa czestotliwosc to ok. 18,2 Hz
jb _graj_blad


in al,klawiatura+1 ; port B kontrolera klawiatury
or al,3 ; ustawiamy bity: 0 i 1 - wlaczamy glosnik
; i bramke od
; licznika nr. 2 czasomierza do glosnika
out klawiatura+1,al



mov si,dx ;zachowujemy DX

mov dx,12h
mov ax,34ddh
div bx ;AX = 1193181 / czestotliwosc, DX=reszta

mov dl,al ;zachowujemy mlodszy bajt dzielnika czestotliwosci



mov al,0b6h

out czasomierz+3,al ;wysylamy komende:
; (bity 7-6) wybieramy licznik nr. 2,
; (bity 5-4) bedziemy pisac najpierw bity 0-7,
; potem bity 8-15
; (bity 3-1) tryb 3: generator fali kwadratowej
; (bit 0) licznik binarny 16-bitowy

mov al,dl ; odzyskujemy mlodszy bajt
out czasomierz+2,al ; port licznika nr. 2 i bity 0-7 dzielnika
; czestotliwosci
mov al,ah
out czasomierz+2,al ; bity 8-15

mov dx,si ;odzyskujemy DX


_graj_pauza:
mov ah,86h
int 15h ; pauza o dlugosci CX:DX mikrosekund

jnc _graj_juz
dec dx
sbb cx,0 ; w razie bledu zmniejszamy CX:DX
jmp short _graj_pauza

_graj_juz:

in al,klawiatura+1
and al,~3 ; zerujemy bity: 0 i 1 - wylaczamy glosnik
; i bramke
out klawiatura+1,al

pop si
pop dx
pop ax
popf
clc

retf


_graj_blad:
pop si
pop dx
pop ax
popf
stc

retf

;**************************************
================================


Jest to moja procedura wytwarzajaca dzwiek w glosniczku (patrz moj inny
artykul). Troche tego jest, co? Ale jest tu duzo spraw, ktore mozna
omowic. Zacznijmy wiec po kolei:

- public... / global...
Funkcje, ktore maja byc "widoczne na zewnatrz" tego pliku, a wiec mozliwe
do uzycia przez inne programy, musza byc zadeklarowane jako "public"
(w NASMie: global). To jest "na wszelki wypadek". Niektore kompilatory
domyslnie traktuja wszystkie symbole jako "public", inne nie. Jesli w
programie bedziemy chcieli korzystac z takiej funkcji, nalezy ja
zadeklarowac jako "extrn" (TASM) lub "extern" (NASM).

- Deklaracja segmentu
Zaden przyzwoity kompilator nie pozwoli na pisanie kodu poza jakimkolwiek
segmentem (no chyba, ze domyslnie zaklada segment kodu, jak NASM).
Normalnie, w "zwyklych" programach, np typu .com, role te pelni
dyrektywa ".code".


assume
Mowimy kompilatorowi, ze CS bedzie wskazywal na ten segment


Gwiazdki
Moga sie wydawac smieszne lub niepotrzebne, ale gdy liczba procedur w pliku
zaczyna siegac 10-20, to NAPRAWDE zwiekszaja czytelnosc kodu, oddzielajac
procedury, dane itd.


Deklaracja procedury (wczesniej zadeklarowanej jako public)
Znak podkreslenia z przodu jest tylko po to, by w razie czego nie byl
identyczny z jakas etykieta w programie korzystajacym z biblioteki.
Deklaracja jest typu "far", zeby zmienic CS na biezacy segment i uniknac
klopotow z 64kB limitem dlugosci skoku (konkretnie to sa to +/- 32kB).


To, czego procedura oczekuje i to, co zwraca.
Jedna procedure latwo zapamietac. Ale co zrobic, gdy jest ich juz 100?
Analizowac kod kazdej, aby sprawdzic, co robi, bo akurat szukamy takiej
jednej....? No przeciez nie.


Dobra technika programowania jest deklaracja stalych w stylu "equ"
(lub #define w C). Zamiast nic nie znaczacej liczby mozna uzyc wiele
znaczacego zwrotu, co przyda sie dalej w kodzie. I nie kosztuje to ani
jednego bajtu. Oczywiscie, ukrywa to czesc kodu (tutaj: numery portow),
ale w razie potrzeby zmienia sie ta wielkosc tylko w 1 miejscu, a nie w 20.


push...
Poza wartosciami zwracanymi nic nie moze byc zmienione! Nieprzyjemnym
uczuciem byloby spedzenie kilku godzin przy odpluskwianiu (debugging)
programu tylko dlatego, ze ktos zapomnial zachowac jakiegos rejestru, prawda?


Sprawdzanie warunkow wejscia, czy sa prawidlowe. Zawsze nalezy
wszystko przewidziec.


Kod procedury. Z punktu widzenia tego artykulu jego tresc jest dla
nas nieistotna.


Punkt(y) wyjscia
Procedura moze miec dowolnie wiele punktow wyjscia. Tutaj zastosowano
dwa, dla dwoch roznych sytuacji:
- parametr byl dobry, procedura zakonczyla sie bez bledow
- parametr byl zly, zwroc informacje o bledzie




Koniec procedury, segmentu i pliku zrodlowego. Slowo "end" nie zawsze
jest konieczne, ale nie zaszkodzi. Wskazuje, gdzie nalezy skonczyc
przetwarzanie pliku.


Mamy wiec juz plik zrodlowy. Co z nim zrobic? Skompilowac, oczywiscie!
tasm naszplik.asm /z /m
(/z - wypisz linie, w ktorej wystapil blad
/m - pozwol na wielokrotne przejscia przez plik)
lub
nasm -f obj -o naszplik.obj naszplik.asm
(-f - okresl format pilku wyjsciowego
-o - okresl nazwe pliku wyjsciowego)

Mamy juz plik "naszplik.obj". W pewnym sensie on juz jest biblioteka!
I mozna go uzywac w innych programach, np. w pliku "program2.asm" mamy:

...
extrn _graj_dzwiek:far ; NASM: "extern _graj_dzwiek"

...
...
mov bx,440
mov cx,0fh
mov dx,4240h
call far ptr _graj_dzwiek ; NASM: call far _graj_dzwiek
...
I mozemy teraz zrobic:

tasm program2.asm /z /m
tlink program2.obj naszplik.obj

lub

nasm -f obj -o program2.obj program2.asm
alink program2.obj naszplik.obj -c- -oEXE -m-

a linker zajmie sie wszystkim za nas - utworzy plik "program2.exe",
zawierajacy takze "naszplik.obj". Jaka z tego korzysc? Plik "program2.asm"
moze bedzie zmieniany w przyszlosci wiele razy, ale "naszplik.asm/.obj"
bedzie ciagle taki sam. A w razie checi zmiany procedury _graj_dzwiek
wystarczy ja zmienic w 1 pliku i tylko jego ponownie skompilowac, bez
potrzeby wprowadzania tej samej zmiany w kilkunastu innych programach.

Te programy wystarczy tylko ponownie skompilowac z nowa "biblioteka",
bez jakichkolwiek zmian kodu.

No dobra, ale co z plikami .lib?
Otoz sa one odpowiednio polaczonymi plikami .obj. I wszystko dziala tak samo.

No ale jak to zrobic?
Sluza do tego specjalne programy, nazywane "librarian" (bibliotekarz). W
pakiecie TASMa znajduje sie program "tlib.exe". Jego wlasnie uzyjemy (dziala
jak LLIB i wszystko robimy tak samo). Pliki .obj, ktore chcemy polaczyc
w biblioteke mozna podawac na linii polecen, ale jest to meczace, nawet
jesli napisze sie plik ".bat" uruchamiajacy tlib. My skorzystamy z innego
rozwiazania.

Programowi mozna na linii polecen podac, aby komendy czytal z jakiegos
pliku. I to wlasnie zrobimy. Piszemy plik "tlib.bat":

tlib.exe naszabibl.lib @lib.txt
i plik "lib.txt" (zwyklym notatnikiem):

+- ..\obj\pisz.obj &
+- ..\obj\wej.obj &
+- ..\obj\procesor.obj &
+- ..\obj\losowe.obj &
+- ..\obj\f_pisz.obj &

+- ..\obj\dzwiek.obj &
+- ..\obj\f_wej.obj &
+- ..\obj\fn_pisz.obj &
+- ..\obj\fn_wej.obj

(uzylem tutaj nazw modulow, ktore skladaja sie na moja biblioteke).
"+-" oznacza 'zamien w pliku dany modul'
"&" oznacza 'sprawdzaj jeszcze w kolejnej linijce'

Przy pierwszym tworzeniu mozna uzyc "+" zamiast "+-", aby uniknac
ostrzezen o uprzedniej nieobecnosci danego modulu w bibliotece.
Teraz uruchamiamy juz tylko "tlib.bat" a w razie potrzeby zmieniamy
tylko "lib.txt".

Gdzie zbobyc narzedzia:

NASM, The Netwide Assembler: http://nasm.sourceforge.net
Alink, http://alink.sf.net/
Lib (LLIB, a nie ten z pakietu Borlanda czy MicroSoftu):
http://www.dunfield.com/downloads.htm (szukaj SKLIB31.ZIP)
http://www2.inf.fh-rhein-sieg.de/~skaise2a/ska/sources.html
Jesli tam go nie ma, to poszukajcie na stronach FreeDOSa: http://www.freedos.org

To chyba wszystko, o czym chcialem powiedziec. Kopia mojej biblioteki
powinna znajdowac sie na stronach, gdzie znalezliscie ten kurs.

Milej zabawy.


Wyszukiwarka

Podobne podstrony:
DOS DIOD TUT
BIBL TUT
DOS PWR TUT
DOS MYSZ TUT
DOS GRAF TUT
DOS SPKR TUT
DOS CPU TUT
DOS BOOT TUT
BIBL TUT
DOS SYS TUT
DOS TSR TUT
DOS BMP TUT
BIBL TUT
FUNFACE DOS OPIS
ART121 tut 2

więcej podobnych podstron