BIBL TUT



#Start Contents

Pisanie wlasnych bibliotek w jezyku asembler

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), 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) i FASMa (Flat Assembler z linkerem ALink i
darmowym bibliotekarzem znalezionym w Internecie (patrz linki na dole
strony).
Napiszmy wiec jakis prosty kod zrodlowy. Oto on:
(przeskocz przykladowy modul biblioteki)
; 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. 18Hz
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/FASMa:
(przeskocz modul w skladni NASMa/FASMa)
; wersja NASM

global _graj_dzwiek
; w FASMie:
; format COFF
; PUBLIC _graj_dzwiek

segment biblioteka_dzwiek ; FASM: section ".text" code

_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. 18Hz
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
; w FASMie: "AND AL, not 3"
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:
1. public... / global...
Funkcje, ktore maja byc "widoczne na zewnatrz" tego pliku, a wiec
mozliwe do uzycia przez inne programy, musza byc zadeklarowane
jako "public" (TASM/FASM) (w NASMie: "global"). To jest "na
wszelki wypadek". Niektore kompilatory domyslnie traktuja
wszystkie symbole jako publiczne, inne nie. Jesli w programie
bedziemy chcieli korzystac z takiej funkcji, nalezy ja
zadeklarowac jako "extrn" (TASM/FASM) lub "extern" (NASM).
2. 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".
3. assume
Mowimy kompilatorowi, ze rejestr CS bedzie wskazywal na ten
segment
4. Gwiazdki lub inne elementy oddzielajace (tu usuniete)
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.
5. Deklaracja procedury (wczesniej zadeklarowanej jako publiczna)
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).
6. 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.
7. 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.
8. push...
Poza wartosciami zwracanymi nic nie moze byc zmienione!
Nieprzyjemnym uczuciem byloby spedzenie kilku godzin przy
odpluskwianiu (debugowaniu) programu tylko dlatego, ze ktos
zapomnial zachowac jakiegos rejestru, prawda?
9. Sprawdzanie warunkow wejscia, czy sa prawidlowe. Zawsze nalezy
wszystko przewidziec.
10. Kod procedury. Z punktu widzenia tego artykulu jego tresc jest
dla nas nieistotna.
11. Punkt(y) wyjscia
Procedura moze miec dowolnie wiele punktow wyjscia. Tutaj
zastosowano dwa, dla dwoch roznych sytuacji:
1. parametr byl dobry, procedura zakonczyla sie bez bledow
2. parametr byl zly, zwroc informacje o bledzie
12. 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, dla NASMa:
nasm -f obj -o naszplik.obj naszplik.asm

(-f - okresl format pliku wyjsciowego
-o - okresl nazwe pliku wyjsciowego)
lub, dla FASMa:
fasm naszplik.asm naszplik.obj

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
; FASM: extrn _graj_dzwiek

...
...
mov bx,440
mov cx,0fh
mov dx,4240h
call far ptr _graj_dzwiek ; NASM: call far _graj_dzwiek
; FASM: call _graj_dzwiek
...

I mozemy teraz zrobic:
tasm program2.asm /z /m
tlink program2.obj naszplik.obj

lub, dla NASMa:
nasm -f obj -o program2.obj program2.asm
alink program2.obj naszplik.obj -c- -oEXE -m-

lub, dla FASMa:
fasm program2.asm program2.obj
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 wsadowy
"tlib.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 zdobyc narzedzia:
1. NASM
2. Alink
3. Lib (LLIB, a nie ten z pakietu Borlanda czy Microsoft-u):
www.dunfield.com/downloads.htm (szukaj SKLIB31.ZIP)
www2.inf.fh-rhein-sieg.de/~skaise2a/ska/sources.html
Jesli tam go nie ma, to poszukajcie na stronach FreeDOS-a

Kopia mojej biblioteki powinna znajdowac sie na stronach, gdzie
znalezliscie ten kurs.
Milej zabawy.

Spis tresci off-line (Alt+1)
Spis tresci on-line (Alt+2)
Ulatwienia dla niepelnosprawnych (Alt+0)


Wyszukiwarka

Podobne podstrony:
BIBL TUT
DOS BIBL TUT
BIBL TUT
ART121 tut 2
phys tut 08
wil pl inst bibl publprac
phys tut 12
DOS DIOD TUT
SYS TUT
ART121 tut 3
MYSZ TUT
PWR TUT
bibl zamowienia publiczne w prawie ue
GRAF TUT
tut?2 sdram vhdl

więcej podobnych podstron