| 
 
 
	
	
		 Wysłany: Nie 16:05, 06 Lis 2005 | 
		     | 
	   | 
 
	
	
		
		
			
			
				
				| Popiol |  
				| Poison Headcrab |  
				 |  
				|   |  
				| Dołączył: 02 Lis 2005 |  
				| Posty: 16 |  
				Przeczytał: 0 tematów
  Ostrzeżeń: 0/5
  |  
				| Skąd: Wygiełzów |  
				 
				 
				
				 | 
			 
			 
 
  | 
			  | 
		 
		
			  | 
			  | 
		 
		 
 
  | 
		
		
			Witam wszystkich i zapraszam na randke z assemblerem. Na dobry początek przedstawiam najkrótszą drogę do odpalenia swojego własnego programu napisanego w assemblerze. 
 
 
Po pierwsze trzeba mieć kompilator. Oto [link widoczny dla zalogowanych] do kompilatora Turbo Assebler, pod którym działają zamieszczone tu programy. 
 
 
Następnie otwieramy edytor tekstowy (taki, który potrafi zapisywać czysty tekst) i wpisujemy kod:
 
 
 	  | Kod: | 	 		  .model tiny
 
.code
 
org 100h
 
 
start:
 
 
  ;wypisanie zmiennej tekst
 
  mov ah, 9
 
  mov dx, offset(tekst)
 
  int 21h
 
 
  ;koniec programu
 
  mov ah, 4ch
 
  int 21h
 
 
  ;definicje
 
  tekst db 'Yo!$'
 
 
end start | 	  
 
 
Zapisujemy plik z rozszerzeniem asm w katalogu z kompilatorem (tasm). Teraz trzeba otworzyć okienko dosowe, przejść do katalogu tasm i uruchomić kompilator poleceniem:
 
 
 	  | Kod: | 	 		  | tasm nazwa_pliku_asm | 	  
 
 
Dzięki temu powstanie plik z rozszerzeniem obj. Następnie odpalamy linkera wpisując:
 
 
 	  | Kod: | 	 		  | tlink nazwa_pliku_obj /t | 	  
 
 
W efekcie dostaniemy plik com, który uruchamiamy i cieszymy się widokiem wypisanego na ekran tekstu. Jak widać programowanie w assemblerze nie jest takie trudne  . Teraz nieco bardziej skomplikowana część artykułu, czyli próba wyjaśnienia jak to działa. 
 
 
Po pierwsze program ładowany jest do pamięci operacyjnej. W naszym prostym programie sytuacja wygląda następująco. Pamięć podzielona jest na 64 KBajtowe segmenty, a nasz program mieści się w jednym takim segmencie. Znajduje się tam zarówno kod jak i dane. Taki model pamięci ustalamy pisząc .model tiny. Co więcej tworzymy program typu com. Aby tak się stało używamy opcji /t przy wywołaniu linkera. Programy typu com to właśnie małe programy mieszczące się w jednym segmencie, w których właściwy kod zaczyna się od 256 baju względem początku segmentu. Wcześniej znajdują się jakieś informacje na temat programu, ale to nas nie interesuje. Polecenie .code oznacza, że zaczynamy segment kodu. W tym przypadku jest to zarazem segment danych. Aby przejść do 256 bajtu uzywamy polecenia org 100h. Litera 'h' oznacza zapis heksadecymalny (szesnastkowy), a więc 100h = 256. W assemblerze możemy używać oprócz heksadecymalnego również zapis dziesiętny (wówczas nie trzeba dodawać żadnej litery) oraz binarny (dodajemy literę 'b'). Możemy zatem napisać równie dobrze org 256. Teraz w końcu możemy zacząć właściwy kod. O pamięci w assemblerze będzie osobny artykuł bo to dość szerokie zagadnienie. 
 
 
Jak widać kod znajduje się w bloku ograniczonym przez start: i end start, które możemy potraktować jak begin i end. w pascalu. Słowo start możemy zastąpić dowolnym innym, to jest tylko nazwa modułu. Komentarze w assemblerze umieszczamy po znaku ;.
 
 
Dalej mamy polecenie mov ah, 9. Jest ono równoznaczne z pascalowym ah := 9. Trzeba pamiętać, że nie zawsze możemy podstawić wartość pod zmienną czy rejestr bespośrednio. Czasami trzeba zrobić coś takiego:
 
 
 	  | Kod: | 	 		  mov ax, wartość
 
mov coś, ax | 	  
 
 
gdzie coś jest zmienną lub rejestrem. O rejestrach na razie powiem tylko tyle, że są to specjalne zmienne procesora. 
 
 
Aby wytłumaczyć po co robimy to i następne podstawienie należy powiedzieć pare słów o przerwaniach. Otóż programując w assemblerze mamy do dyspozycji całą listę funkcji, które np. zmieniają ustawienia BIOSu, albo wypisują tekst na ekran, albo wczytują dane z klawiatury itp. Funkcję taką wykonuje się przez wywołanie odpowiedniego przerwania. Jest tego naprawdę sporo, dlatego w kolejnych artykułach będę opisywał różne ciekawe przerwania. 
 
 
Przerwania wywołuje się poleceniem int numer_przerwania. W naszym programie użyliśmy jednego przerwania o numerze 21h. Wywołuje ono jedną z funkcji dosowych. O tym która funkcja zostanie ostatecznie wywołana decyduje zawartość rejestru ah. Dlatego właśnie wykonujemy podstawienie mov ah, 9. Funkcja dosowa o numerze 9 to funkcja 'Print String'. Wypisuje ona na standardowe wyjście string, którego adres znajduje się w rejestrach ds i dx. Rejestr ds to rejestr segmentu danych (wskazuje na segment, w którym są dane). Dzięki temu, że tworzymy plik com, rejestr ten ma od razu ustawioną właściwą wartość. Rejestr dx ma natomiast, w tym przypadku, zawierać tzw. offset odpowiadający stringowi. Offset to przesunięcie w pamięci względem początku segmentu. Zatem segment i offset stanowią razem adres konkretnej komórki pamięci. W tym przypadku rejestry ds i dx stanowią adres początku stringu, który chcemy wypisać. Offset, który potrzebujemy dostajemy pisząc offset(tekst), gdzie tekst to nazwa naszego stringu. Pozostaje pytanie, gdzie jest koniec stringu. Otóż ogranicznikiem dla stringu jest znak '$'.
 
 
Przy drugim wywołaniu przerwania 21h, wartość w rejestrze ah wynosi 4ch (przypominam, że c odpowiada liczbie 12 w zapisie szesnastkowym). Jest to funkcja, która kończy działanie programu i sprząta po nim (czyści pamięc i takie tam).
 
 
Ufff doszliśmy do deklaracji zmiennych, a właściwie jednej zmiennej tekst. Kluczowym słowem jest tutaj db - skrót od define byte. Jak łatwo się domyśleć definiuje ono jeden bajt. Składnia:
 
 
 	  | Kod: | 	 		  | nazwa db wartość[, wartość...] | 	  
 
 
Jeśli napiszemy kilka wartości to zdefinujemy kilka bajtów, a nazwa będzie wskazywała na pierwszy. W ten sposób utworzymy tablicę. Natomiast zapis:
 
 
 
 
jest równoważny takiemu:
 
 
 	  | Kod: | 	 		  | tekst db 'Y','o','!','$' | 	  
 
 
Znaki zaś są zamieniane na kody ASCII. To zupełnie tak jakbyśmy napisali w C++: char tekst[4] = "Yo!", tylko w C++ automatycznie dodawany jest znak końca stringu. Jeśli jesteśmy przy deklarowaniu zmiennych to napiszę od razu jak zdefiniować większą zmienną:
 
 
dw - define word = 2 bajty
 
dd - define double word = 4 bajty
 
df - define far word = 6 bajtów
 
dq - define quad word = 8 bajtów
 
dt - define temp word = 10 bajtów
 
 
I jeszcze jedna ciekawa rzecz. Pokazaliśmy jak można zdefiniować kilkuelementową tablicę. Natomiast jeśli chcemy mieć większą tablicę robimy tak:
 
 
 	  | Kod: | 	 		  | nazwa db rozmiar dup(wartość) | 	  
 
 
Powstanie tabilca bajtów, w której ilość elementów = rozmiar, a początkowe wartości są równe wartość. Jeśli nie chcemy ustalać początkowej wartości możemy wpisać dup(?). Aby uciąć ewentualne spekulacje dodam, że dup to skrót od duplicate  . A oto tablica dwuwymiarowa n na m elementów, inicjalizowana zerami:
 
 
 	  | Kod: | 	 		  | nazwa db n dup(m dup (0)) | 	  
 
 
Do wartości zmiennej odnosimy się umieszczając jej nazwę w nawiasach kwadratowych, np:
 
 
 	  | Kod: | 	 		  mov [zmienna], ax ; zmienna := ax
 
mov ax, [tablica+10] ; ax := tablica[10]
 
mov ax, [tablica+5*m+4] ; ax := tablica[5,4] z tym, że m jest konkretną wartością, a nie zmienną | 	  
 
 
Pamiętajcie, że muszą zgadzać się typy. To co podstawiamy musi mieć tyle samo bajtów co to pod co podstawiamy. Jeśli chcemy pod zmienną podstawić wartość np 2 to robimy to za pośrednictwem rejestru ax, czyli najpierw do ax i dopiero z ax do zmiennej. Skoro już przy tym jesteśmy to przyda się podstawowa wiedza na temat rejestrów ogólnego przeznaczenia. Pierwszym takim rejestrem jest akumulator. Ma on następującą konstrukcję:
 
 
rax = 64 bity = 32 starsze bity + eax
 
eax = 32 bity = 16 starszych bitów + ax
 
ax = 16 bitów = ah (8 starszych bitów) + al (8 młodszych bitów)
 
 
Analogicznie wyglądają inne rejestry ogólnego przeznaczenia: bx (bazowy), cx (licznik), dx (danych).
 
 
Zanim zaczniemy coś ciekawego programować trzeba jeszcze wspomnieć o kilku ważnych poleceniach.
 
 
Operacje arytmetyczne:
 
add x, y ; x := x + y
 
sub x, y ; x := x - y
 
dec x ; x := x - 1
 
inc x ; x := x + 1
 
div x ; al := ax div x, ah := ax mod x, dla x wielkości bajta
 
div x ; ax := dx:ax div x, dx := dx:ax mod x, dla x wielkości 2 bajtów
 
div x ; eax := edx:eax div x, edx := edx:eax mod x, dla x wielkości 4 bajtów
 
idiv x ; to samo co div ale x jest ze znakiem
 
mul x ; ax := al * x, dla x wielkości bajta
 
mul x ; dx:ax := ax * x, dla x wielkości 2 bajtów
 
mul x ; edx:eax := eax * x, dla x wielkości 4 bajtów
 
imul x ; to samo co mult ale x jest ze znakiem
 
neg x ; x := -x
 
 
Operacje bitowe
 
and x, y ; x := x and y na każdym bicie
 
or x, y ; x := x or y na każdym bicie
 
xor x, y ; x := x xor y na każdym bicie
 
shl x, n ; przesuwa bity w lewo o n, uzupełniając zerami
 
shr x, n ; przesuwa bity w prawo o n, uzupełniając zerami
 
 
Porównanie dwóch wartości: 
 
cmp x, y ; porównuje x i y, a wynik sprawdzamy instrukcją skoku
 
 
Skoki warunkowe odnoszą się do wartości ostatniego wyrażenia i wykonują skok jeśli zachodzi warunek: 
 
ja etykieta ; warunek := x > y (bez znaku) dla polecenia cmp albo x > 0
 
jb etykieta ; warunek := x < y (bez znaku) dla polecenia cmp albo x < 0
 
jg etykieta ; warunek := x > y (ze znakiem) dla polecenia cmp
 
jl etykieta ; warunek := x < y (ze znakiem) dla polecenia cmp
 
je etykieta ; warunek := x == y dla polecenia cmp
 
jz etykieta ; warunek := x == 0
 
jmp etykieta ; skok bezwarunkowy
 
 
Możemy też zaprzeczać powyższe warunki dodając literę 'n' w poleceniu, np:
 
jna etykieta ; warunek := x <= y (bez znaku) dla polecenia cmp albo x <= 0
 
 
etykiety robi się tak samo jak np w c++ czyli
 
 
 
 
Dobra, na razie wystarczy. Na koniec jeszcze jeden program.
 
 
 	  | Kod: | 	 		  .model tiny
 
.code
 
org 100h
 
 
start:
 
 
  ;inicjalizacja grafiki 320x200 256kol
 
  mov ax, 0013h
 
  int 10h
 
 
rysuj:
 
  
 
  ;obliczenie funkcji
 
  mov dx, 0000h
 
  mov ax, [x]
 
  imul [x]
 
  mov bx, 0080h;
 
  div bx;
 
  mov dx, 200
 
  sub dx, ax
 
  
 
  ;putpixel al=kolor cx=x dx=y
 
  mov ah, 0ch
 
  mov al, 100
 
  mov cx, 00a0h
 
  add cx, [x]
 
  int 10h 
 
 
  ;x-- i sprawdzenie warunku końca
 
  dec [x]
 
  cmp [x], -160
 
  jnl rysuj
 
 
  ;koniec programu
 
  mov ah, 4ch
 
  int 21h
 
 
  ;definicje
 
  x dw 160;
 
 
end start  | 	 
  | 
		 
		  | 
	 
	
		
		
			
    			 
    			 
    			     
  Post został pochwalony 0 razy
  Ostatnio zmieniony przez Popiol dnia Pią 23:43, 11 Lis 2005, w całości zmieniany 1 raz
    			     
    			 
			 | 
		 
		  | 
	 
	  | 
 
	 | 
 
 
     | 
	
	
		 Wysłany: Nie 0:12, 13 Lis 2005 | 
		     | 
	   | 
	
	
		
		
			
			
				
				| Popiol |  
				| Poison Headcrab |  
				 |  
				|   |  
				| Dołączył: 02 Lis 2005 |  
				| Posty: 16 |  
				Przeczytał: 0 tematów
  Ostrzeżeń: 0/5
  |  
				| Skąd: Wygiełzów |  
				 
				 
				
				 | 
			 
			 
 
  | 
			  | 
		 
		
			  | 
			  | 
		 
		 
 
  | 
		
		
			Musze dodać jedną rzecz na temat pętli. Jeśli chcemy, żeby dany fragment kodu wykonał się n razy to możemy napisać tak:
 
 
 	  | Kod: | 	 		  mov cx, n ; n - jakaś wartość np 5 albo [x]
 
petla:
 
  ...
 
loop petla | 	  
 
 
Polecenie loop zmniejsza zawrtość rejestru cx o 1 i wraca do podanej etykiety jeśli cx <> 0.
 
 
A teraz kolejny krok ku zdobyciu nieograniczonej władzy nad komputerem: funkcje. Wywoływanie funkcji, a właściwie procedur w assemblerze wygląda tak:
 
 
 	  | Kod: | 	 		  .model tiny
 
.code
 
org 256
 
 
start:
 
 
  call wypiszA ; wywołanie procedury wypiszA
 
  jmp exit
 
 
;FUNKCJE_________________________
 
  
 
  ;procedura wypiszA
 
  wypiszA proc
 
    ;zapisanie modyfikowanych rejestrów
 
    push ax
 
    push dx
 
    ;wypisanie litery A
 
    mov ah, 02h
 
    mov dl, 'A'
 
    int 21h
 
    ;przywrócenie zapisanych rejestrów
 
    pop dx
 
    pop ax
 
    ;powrót z procedury
 
    ret 
 
  wypiszA endp ; koniec definicji procedury
 
 
;KONIEC__________________________
 
 
exit:
 
  mov ah, 4ch
 
  int 21h
 
 
end start | 	  
 
 
Pierwszym ważnym poleceniem jest call nazwa_procedury. Zapisuje ono na stosie adres następnej linii kodu, a następnie skacze do procedury. Definicja procedury zaczyna się od nazwa_procedury proc, a kończy na nazwa_procedury endp. Aby poprawnie wyjść z procedury trzeba użyć polecenia ret. Pobiera ono ze stosu adres, który wrzuciliśmy tam poleceniem call i przechodzi pod ten adres.
 
 
W tym miejscu wypada powiedzieć pare słów o stosie. Każdy program oprócz segmentu kodu i segmentu danych posiada jeszcze coś takiego jak segment stosu. Generalnie czym jest stos każdy wie (mam nadzieje). Ten stos w assemblerze służy do przechowywania pewnych informacji podczas wywoływań procedur, przede wszystkim adresu powrotnego. Aby położyć coś na stosie piszemy push coś, gdzie coś może być czymkolwiek o rozmiarze dwóch bajtów. Aby zdjąć elemant ze stosu piszemy pop cel, gdzie cel to zmienna lub rejestr o wielkości dwóch bajtów.
 
 
Wewnątrz procedury użyłem operacji push i pop, aby po jej wykonaniu zawartość rejestrów ax i dx pozostała nie zmieniona. W tym małym programie akurat nie ma to znaczenia, ale w większych programach nie zmienianie zawartości rejestrów w funkcjach jest przyjemną cechą. 
 
 
Do wypisania znaku użyte zostało przerwanie dosowe z serii 21h, o numerze 2. Przerwanie to wypisuje znak, którego kod ASCII jest w rejestrze dl.
 
 
Jak widać wywoływanie procedur, które nie mają parametrów i nic nie zwracają jest całkiem proste. Przypadek z parametrami i zwracaną wartością już nie jest taki przyjemny. Nie da się przesłać parametrów tak jak np w C. Można przesłać parametr np przez rejestry, czyli przed wywołaniem procedury wstawiamy coś do rejestru, a procedura z tego korzysta. Tak samo możemy zwracać wartości, tzn procdura ustawia zawartość rejestru, a my po wywołaniu procedury z tego korzystamy. Dokładnie tak jest w przerwaniach. Możemy też wysłać parametr wrzucając go na stos. Musimy jednak pamiętać, że w momencie wywołania procedury na stos idzie jeszcze adres. W przypadku wywołania "bliskiego" idzie tylko offset, a więc dwa bajty, bo funkcja znajduje się w tym samym segmencie co kod. Wewnątrz procedury musimy najpierw ściągnąć offset do rejestru indeksowego, np di. Dopiero wtedy możemy pobrać ze stosu parametr. Na koniec, przed powrotem z procedury, musimy wrzucić na stos adres powrotny. Możemy w podobny sposób zwracać wartości. Umieszczamy je wtedy na stosie przed umieszczeniem adresu powrotnego. Wygląda to tak:
 
 
 	  | Kod: | 	 		  .model tiny
 
.code
 
org 256
 
 
start:
 
 
  ;wrzucamy na stos parametr funkcji, 13 to kod ASCII entera
 
  push 13
 
  call czytajDo
 
  cmp cx, 0
 
  jng exit
 
  petla:
 
    ;zdejmujemy ze stosu wartości funkcji
 
    pop dx
 
    call wypiszZnak
 
    loop petla
 
  jmp exit
 
 
;FUNKCJE_________________________
 
  
 
  czytajDo proc
 
  ;modyfikuje di, ax, bx, cx
 
  ;cx - ilość przeczytanych znaków
 
 
    ;zdejmujemy adres powrotny
 
    pop di
 
    ;zdejmujemy parametr
 
    pop bx
 
    mov cx, 0
 
    czytaj:
 
      ;wczytaj znak
 
      mov ah, 00h
 
      int 16h
 
      ;umieść go na stosie
 
      push ax
 
      inc cx
 
      cmp al, bl
 
      jne czytaj
 
    ;ostatni wczytany znak jest niepotrzebny
 
    pop ax
 
    dec cx
 
    ;wrzucamy na stos adres powrotny
 
    push di
 
    ;wracamy
 
    ret
 
 
  czytajDo endp
 
;_______________
 
 
  wypiszZnak proc
 
  ;modyfikuje ah
 
 
    mov ah, 02h
 
    int 21h
 
    ret
 
 
  wypiszZnak endp
 
 
;KONIEC__________________________
 
 
exit:
 
  mov ah, 4ch
 
  int 21h
 
 
end start | 	  
 
 
Ten program wczytuje znaki z klawiatury aż do znaku, który podajemy jako parametr w procedurze czytajDo. Tutaj jest to znak o kodzie 13 czyli enter. Kod ASCII jest wprawdzie jedno bajtowy, ale w rzeczywistości wrzucane są dwa bajty, a kod jest bajtem mniej znaczącym. Jak widać w procedurze czytajDo najpierw ściągamy ze stosu adres powrotny, a następnie parametr. Procedura ta ma zwrócić wczytane znaki przez stos. Rejestr cx będzie przechowywał ilość wczytanych znaków. 
 
 
Do wczytywania używam przerwania z serii 16h (Keyboard BIOS Services) o numerze 0. Ta funkcja czeka, aż jakiś przycisk klawatury zostanie wciśnięty i zwraca do niego kod w rejestrze ax. Jeśli jest to zwykły znak to w al jest jego kod ASCII.
 
 
A więc wczytujemy znaki, umieszczając je od razu na stosie, aż wczytamy znak, który jest parametrem procedury. Po wyjściu z pętli usuwamy jeszcze ostatni wczytany znak i wrzucamy na stos adres powrotny. Po powrocie do głównego kodu programu wypisujemy znaki ze stosu. Są one ustawione w odwrotnej kolejności niż były wpisane, co wynika z własności stosu.
 
 
Kilka uwag do procedury czytajDo. Po pierwsze jeśli chcemy przekazać procedurze znak, lepiej użyć do tego celu rejestru, tak jak jest to w przypadku procedury wypiszZnak. Po drugie jeśli chcemy zwrócić wartość, która nie mieści się w rejestrach, to lepiej użyć do tego celu zmiennej. Generalnie można powiedzieć, że używanie stosu do wysyłania i zwracania wartości jest poronionym pomysłem. Nic dziwnego w końcu sam na to wpadłem  . | 
		 
		  | 
	 
	
		
		
			
    			 
    			 
    			     
  Post został pochwalony 0 razy
    			     
    			 
			 | 
		 
		  | 
	 
	  | 
	 | 
	
	
		 Wysłany: Nie 0:33, 13 Lis 2005 | 
		     | 
	   | 
	
	
		
		
			
			
				
				| Popiol |  
				| Poison Headcrab |  
				 |  
				|   |  
				| Dołączył: 02 Lis 2005 |  
				| Posty: 16 |  
				Przeczytał: 0 tematów
  Ostrzeżeń: 0/5
  |  
				| Skąd: Wygiełzów |  
				 
				 
				
				 | 
			 
			 
 
  | 
			  | 
		 
		
			  | 
			  | 
		 
		 
 
  | 
		
		
			A teraz dla odmiany coś pożytecznego i ciekawego:
 
 
 	  | Kod: | 	 		  .model tiny
 
.code
 
org 256
 
 
start:
 
 
  mov dx, 0000h
 
  call skocz
 
  call cout
 
  db 'Sterowanie: <- w lewo, -> w prawo, Esc - koniec',10,13
 
  db 'Punkty: ',10,13
 
  db 80 dup('_'),0
 
  call rysujRakiete 
 
  main:
 
    call rysujPilke
 
    call piszPunkty 
 
    call klawiatura
 
    mov cx, 20000
 
    sleep:
 
      int 1Ch;
 
      loop sleep
 
    cmp koniec, 0
 
    je main
 
  jmp exit
 
 
;FUNKCJE_________________________
 
 
;******************************** 
 
  cout proc
 
  ;wypisuje string, który znajduje się po wywołaniu funkcji
 
    pop di
 
    xor bx, bx ; bl = 0, bl jest znakiem końca stringu
 
    cmp [di], bl
 
    je cout_ret
 
    cout_petla:
 
      mov al, [di]
 
      mov ah, 0Eh
 
      int 10h
 
      inc di
 
      cmp [di], bl
 
      jne cout_petla
 
  cout_ret:
 
    inc di
 
    push di
 
    ret
 
  cout endp
 
;********************************
 
 
 
;********************************
 
  wypiszCyfre proc
 
  ;wypisuje cyfrę, która jest w al
 
    add al, 48
 
    mov ah, 0Ah
 
    xor bh, bh
 
    mov cx, 1
 
    int 10h
 
    ret
 
  wypiszCyfre endp
 
;********************************
 
 
;********************************
 
  skocz proc
 
  ;ustawia kursor na pozycji (dl,dh)
 
    mov ah, 02h
 
    mov bh, 0
 
    int 10h
 
    ret
 
  skocz endp
 
;********************************
 
 
;********************************
 
  piszPunkty proc
 
  ;wypisuje ilość punktów
 
    mov ax, punkty
 
    mov bl, 10
 
    div bl
 
    push ax
 
    mov al, ah
 
    mov dx, 010Bh
 
    call skocz
 
    call wypiszCyfre
 
    pop ax
 
    mov ah, 0
 
    div bl
 
    push ax
 
    mov al, ah
 
    mov dx, 010Ah
 
    call skocz
 
    call wypiszCyfre
 
    pop ax
 
    mov dx, 0109h
 
    call skocz
 
    call wypiszCyfre
 
    ret
 
  piszPunkty endp
 
;********************************
 
 
;********************************
 
  rysujRakiete proc
 
  ;rysuje "rakietę renisową"
 
    mov dl, rakietax
 
    mov dh, 23
 
    call skocz
 
    call cout
 
    db '======',0
 
    ret
 
  rysujRakiete endp
 
;********************************
 
 
;********************************
 
  zmazRakiete proc
 
  ;maże "rakietę tenisową"
 
    mov dl, rakietax
 
    mov dh, 23
 
    call skocz
 
    call cout
 
    db '      ',0
 
    ret
 
  zmazRakiete endp
 
;********************************
 
 
;********************************
 
  klawiatura proc
 
  ;odczytuje naciśnięty klawisz
 
    mov ah, 01h
 
    int 16h
 
    je klaw_ret
 
    mov ah, 0h
 
    int 16h
 
    cmp ah, 4Bh ; kursor w lewo
 
    je wlewo
 
    cmp ah, 4Dh ; kursor w prawo
 
    je wprawo
 
    cmp ah, 01h ; Esc
 
    je wyjscie
 
    ret
 
  wlewo:
 
    cmp rakietax, 0
 
    je klaw_ret
 
    call zmazRakiete
 
    dec rakietax
 
    call rysujRakiete
 
    ret
 
  wprawo:
 
    cmp rakietax, 73
 
    je klaw_ret
 
    call zmazRakiete
 
    inc rakietax
 
    call rysujRakiete
 
    ret
 
  wyjscie:
 
    mov koniec, 1
 
  klaw_ret:
 
    ret
 
  klawiatura endp
 
;********************************
 
 
;********************************
 
  zmazPilke proc
 
  ;maże pilkę
 
    mov dl, pilkax
 
    mov dh, pilkay
 
    call skocz
 
    call cout
 
    db ' ',0
 
    ret
 
  zmazPilke endp
 
;********************************
 
 
;********************************
 
  rysujPilke proc
 
  ;rysuje piłkę
 
    cmp czas, 0
 
    je rysujemyPilke
 
    dec czas
 
    ret
 
  rysujemyPilke:
 
    mov ah, klatki
 
    mov czas, ah
 
    cmp pilkay, 23
 
    je aut
 
    cmp pilkay, 3
 
    je gora
 
  w1:
 
    cmp pilkax, 0
 
    je lewo
 
  w2:
 
    cmp pilkax, 78
 
    je prawo
 
  w3:
 
    cmp pilkay, 22
 
    je dol
 
    jmp rysuj
 
  aut:
 
    call zmazRakiete
 
    call zmazPilke
 
    mov punkty, 0
 
    mov rakietax, 37
 
    mov pilkax, 40
 
    mov pilkay, 21
 
    mov wektorx, 1
 
    mov wektory, -1
 
    call pauza
 
    call rysujRakiete
 
    ret
 
  dol:
 
    mov al, pilkax
 
    add al, wektorx
 
    cmp rakietax, al
 
    ja rysuj
 
    sub al, 5
 
    cmp rakietax, al
 
    jl rysuj
 
    mov wektory, -1
 
    inc punkty
 
    jmp rysuj
 
  gora:
 
    mov wektory, 1
 
    jmp w1
 
  lewo:
 
    mov wektorx, 1
 
    jmp w2
 
  prawo:
 
    mov wektorx, -1
 
    jmp w3
 
  rysuj:
 
    call zmazPilke
 
    mov al, wektorx
 
    mov ah, wektory
 
    add pilkax, al
 
    add pilkay, ah
 
    mov dl, pilkax
 
    mov dh, pilkay
 
    call skocz
 
    call cout
 
    db 'o',0
 
    ret
 
  rysujPilke endp
 
;********************************
 
 
;********************************
 
  pauza proc
 
  ;"game over"
 
    mov dx, 0C14h
 
    call skocz
 
    call cout
 
    db 'GAME OVER cieniasie haha :P (nacisnij spacje)',0
 
  czekaj:
 
    mov ah, 10h
 
    int 16h
 
    cmp ah, 01h
 
    je exit
 
    cmp ah, 39h
 
    jne czekaj
 
    call skocz
 
    call cout
 
    db '                                             ',0
 
    ret
 
  pauza endp
 
;********************************
 
 
;ZMIENNE_________________________
 
 
  punkty dw 0 ; ilość punktów
 
  rakietax db 37 ; położenie rakiety tenisowej
 
  pilkax db 40 ; polozenie pilki
 
  pilkay db 21 ; polozenie pilki
 
  wektorx db 1 ; wektor ruchu piłki
 
  wektory db -1 ; wektor ruchu piłki
 
  koniec db 0 ; jeśli <> 0 to koniec programu
 
  czas db 2 ; odlicza klatki
 
  klatki db 2 ; co tyle klatek rysuje się pilka
 
 
;KONIEC__________________________
 
 
exit:
 
  mov ah, 4ch
 
  int 21h
 
 
end start | 	 
  | 
		 
		  | 
	 
	
		
		
			
    			 
    			 
    			     
  Post został pochwalony 0 razy
  Ostatnio zmieniony przez Popiol dnia Wto 23:49, 22 Lis 2005, w całości zmieniany 2 razy
    			     
    			 
			 | 
		 
		  | 
	 
	  | 
	 |