pRoGraMerZ


Forum pRoGraMerZ Strona Główna -> Assembler -> Pierwsze kroki Idź do strony 1, 2, 3, 4  Następny
Napisz nowy temat  Odpowiedz do tematu Zobacz poprzedni temat :: Zobacz następny temat 
Pierwsze kroki
PostWysł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 Smile. 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:

Kod:
tekst db 'Yo!$'


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 Razz. 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

Kod:
etykieta:


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
Zobacz profil autora
PostWysłany: Czw 22:19, 10 Lis 2005
bednarz
Administrator

 
Dołączył: 20 Paź 2005
Posty: 6
Przeczytał: 0 tematów

Ostrzeżeń: 0/5





no w koncu mi sie udalo odpalic to cudo chociaz nie do konca bo nie wiem jaki mial byc efekt tego 2. programu U mnie polowa ekranu jest na zielono a polowa na czarno Cos mi sie zdaje ze nie o to chodzilo Question


Post został pochwalony 0 razy
Zobacz profil autora
PostWysłany: Pią 22:28, 11 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





Faktycznie nie o to chodziło. Drugi program ma rysować parabolę. U mnie działa, więc troche mnie to niepokoi, że u ciebie nie. Opiszę użyte przerwania: ustawiające tryb graficzny i wyświetlające piksel. Proponuję poeksperymentwać z trybami i spróbować wyświetlić jeden piksel o współrzędnych np (100, 100), a niepotrzebny kod wywalić. Ewentualnie uruchom system w trybie MSDOS i zobacz czy tam działa.

Oba przerwania należą do grupy przerwań Video BIOS Services, które wywołuje się poleceniem int 10h. O tym, która dokładnie funkcja się wykona decyduje zawartość rejestru ah. Jeśli ah = 0 to wykonamy funkcję Set Video Mode. Funkcja ta ustawia tryb wyświetlania, w zależności od zawartości rejestru al. Oba rejestry ah i al można ustawić jednocześnie, podstawiając wartość pod ax (ax = ah al). Przykładowe tryby wyświetlania:

al = 00h - 40x25 biało-czarny, tekst
al = 03h - 80x25 16 kolorów, tekst
al = 06h - 640x200 biało-czarny, grafika
al = 0dh - 320x200 16 kolorów, grafika
al = 12h - 640x480 16 kolorów, grafika
al = 13h - 320x200 256 kolorów, grafika

Aby wykonać funkcję wyświetlającą piksel musimy do ah wstawić wartość 0ch i wywołać int 10h. Parametry tej funkcji to:

al - kolor
bh - numer strony (aktualna strona to chyba standardowo 0)
cx - współrzędna x
dx - współrzędna y


Post został pochwalony 0 razy
Zobacz profil autora
PostWysł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 Very Happy.


Post został pochwalony 0 razy
Zobacz profil autora
PostWysł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
Zobacz profil autora
Pierwsze kroki
Forum pRoGraMerZ Strona Główna -> Assembler
Możesz pisać nowe tematy
Możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz głosować w ankietach
Wszystkie czasy w strefie EET (Europa)  
Strona 1 z 4  
Idź do strony 1, 2, 3, 4  Następny
  
  
 Napisz nowy temat  Odpowiedz do tematu  


fora.pl - załóż własne forum dyskusyjne za darmo
Powered by phpBB © 2001-2003 phpBB Group
Theme created by Vjacheslav Trushkin
Regulamin