   #Start Prev Next Contents

   Jak pisac programy w jezyku asembler?

Czesc 4 - Pierwsze programy, czyli przelamywanie pierwszych lodow

   Znamy juz rejestry, troche instrukcji i zasad. No ale teoria jest
   niczym bez praktyki. Dlatego w tej czesci przedstawie kilka wzglednie
   prostych programow, ktore powinny rozbudzic wyobraznie tworzenia.

   Ten program spyta sie uzytkownika o imie i przywita sie z nim:
   (przeskocz program pytajacy o imie)
        ; Program witajacy sie z uzytkownikiem po imieniu
        ;
        ; Autor: Bogdan D.
        ; kontakt: bogdandr (at) op (dot) pl
        ;
        ; kompilacja:
        ; nasm -f bin -o czesc.com czesc.asm
        ;
        ; kompilacja FASM:
        ; fasm czesc.asm czesc.com

        org 100h

                mov     ah, 9           ; funkcja wyswietlania na ekran
                mov     dx, jak_masz    ; co wyswietlic
                int     21h             ; wyswietl

                mov     ah, 0ah ; funkcja pobierania danych z klawiatury
                mov     dx, imie        ; bufor na dane
                int     21h             ; pobierz dane

                mov     ah, 9
                mov     dx, czesc
                int     21h             ; wyswietl napis "Czesc"

                mov     ah, 9
                mov     dx, imie+2      ; adres wpisanych danych
                int     21h             ; wyswietl wpisane dane

                mov     ax, 4c00h
                int     21h

        jak_masz        db      "Jak masz na imie? $"
        imie    db 20           ; maksymalna liczba znakow do pobrania
                db 0            ; tu dostaniemy, ile znakow pobrano
                times 22 db "$" ; miejsce na dane

        czesc           db      10, 13, 10, 13, "Czesc $"

   Powyzszy program korzysta z jeszcze nieomowionej funkcji numer 10
   (0Ah) przerwania DOSa. Oto jej opis z listy przerwan Ralfa Brown'a -
   RBIL:
   (przeskocz opis int 21h, ah=0ah)
        INT 21 - DOS 1+ - BUFFERED INPUT
                AH = 0Ah
                DS:DX -> buffer (see #01344)
        Return: buffer filled with user input

        Format of DOS input buffer:
        Offset  Size    Description     (Table 01344)
         00h    BYTE    maximum characters buffer can hold
         01h    BYTE    (call) number of chars from last input which
                                may be recalled
                        (ret) number of characters actually read,
                                excluding CR
         02h  N BYTEs   actual characters read, including the
                                final carriage return

   Jak widac, korzystanie z niej nie jest trudne. Wystarczy stworzyc
   tablice bajtow na znaki czytane z klawiatury. Na poczatku tablicy
   podajemy, ile maksymalnie znakow chcemy wczytac. Drugi bajt ustawiamy
   na zero, by czytac tylko na biezaco wprowadzane znaki, a nie to, co
   jeszcze moze tkwic w DOS-owym buforze wejsciowym.
     _________________________________________________________________

   Kolejny program wypisuje na ekranie rejestr flag w postaci dwojkowej.
   Zanim mu sie przyjrzymy, potrzebna bedzie nam informacja o funkcji 0Eh
   przerwania 10h (opis bierzemy oczywiscie z RBIL):
   (przeskocz opis int 10h, ah=0eh)
        INT 10 - VIDEO - TELETYPE OUTPUT
                AH = 0Eh
                AL = character to write
                BH = page number
                BL = foreground color (graphics modes only)
        Return: nothing
        Desc:   display a character on the screen, advancing the
                 cursor and scrolling the screen as necessary
        Notes:  characters 07h (BEL), 08h (BS), 0Ah (LF), and 0Dh (CR)
                 are interpreted and do the expected things

   Dla nas zawartosc BX nie bedzie istotna. A ta funkcja po prostu
   wypisuje na ekran jakis znak. No, teraz wreszcie mozemy przejsc do
   programu. Oto on (flagi.asm):
   (przeskocz program wypisujacy flagi)
; Program wypisujacy flagi w postaci dwojkowej
;
; Autor: Bogdan D.
; kontakt:  bogdandr (at) op (dot) pl
;
; kompilacja:
; tasm flagi.asm
; tlink flagi.obj /t


.model tiny             ; to bedzie maly program
.code                   ; tu zaczyna sie segment kodu
.386                    ; bedziemy tu uzywac rejestrow 32-bitowych.
                        ; .386 MUSI byc po .code !

org 100h                ; to bedzie program typu .com

main:                   ; etykieta jest dowolna, byleby zgadzala sie
                        ; z ta na koncu

        pushfd          ; 32 bity flag ida na stos

        mov ax,0e30h    ; AH = 0eh, czyli funkcja wyswietlania,
                        ; AL = 30h = kod ASCII cyfry zero

        pop esi         ; flagi ze stosu do ESI

        mov cx,32       ; tyle bitow i tyle razy trzeba przejsc
                        ; przez petle

petla:                  ; etykieta oznaczajaca poczatek petli.

        and al,30h      ; upewniamy sie, ze AL zawiera tylko 30h,
                        ; co zaraz sie moze zmienic. A dokladniej,
                        ; czyscimy bity 0-3, z ktorych bit 0 moze
                        ; sie zaraz zmienic

        shl esi,1       ; Przesuwamy bity w ESI o 1 w lewo.
                        ; 31 bit ESI idzie
                        ; do Carry Flag (CF)

        adc al,0        ; ADC - add with carry. Do AL dodaj
                        ; 0 + wartosc CF.

                        ; jesli CF (czyli 31 bit ESI) = 1,
                        ; to AL := AL+1, inaczej AL bez zmian
        int 10h         ; funkcja 0e, wyswietl znak w AL,
                        ; czyli albo zero (30h) albo jedynke (31h)

        loop petla      ; przejdz na poczatek petli,
                        ; jesli nie skonczylismy

        mov ah,4ch      ; funkcja wyjscia do DOS
        int 21h         ; wychodzimy

end main                ; koniec programu. Ta sama etykieta, co na poczatku.
     _________________________________________________________________

   Wersje NASM i FASM:
   (przeskocz wersje NASM/FASM programu)
; Program wypisujacy flagi w postaci dwojkowej
;
; Autor: Bogdan D.
; kontakt:  bogdandr (at) op (dot) pl
;
; kompilacja NASM:
;   nasm -o flagi.com -f bin flagi.asm
;
; kompilacja FASM:
;   fasm flagi.asm flagi.com


org 100h                ; to bedzie program typu .com

main:                   ; etykieta dowolna, nawet niepotrzebna

        pushfd          ; 32 bity flag ida na stos

        mov ax,0e30h    ; AH = 0eh, czyli funkcja wyswietlania,
                        ; AL = 30h = kod ASCII cyfry zero

        pop esi         ; flagi ze stosu do ESI

        mov cx,32       ; tyle bitow i tyle razy trzeba przejsc
                        ; przez petle

petla:                  ; etykieta oznaczajaca poczatek petli.

        and al,30h      ; upewniamy sie, ze AL zawiera tylko 30h,
                        ; co zaraz sie moze zmienic. A dokladniej,
                        ; czyscimy bity 0-3, z ktorych bit 0
                        ; moze sie zaraz zmienic

        shl esi,1       ; Przesuwamy bity w ESI o 1 w lewo.
                        ; 31 bit ESI idzie do flagi CF

        adc al,0        ; ADC - add with carry. Do AL dodaj
                        ; 0 + wartosc CF.
                        ; jesli CF (czyli 31 bit ESI) = 1,
                        ; to AL := AL+1, inaczej AL bez zmian

        int 10h         ; funkcja 0e, wyswietl znak w AL,
                        ; czyli albo zero (30h) albo jedynke (31h)

        loop petla      ; przejdz na poczatek petli,
                        ; jesli nie skonczylismy

        mov ah,4ch      ; funkcja wyjscia do DOS
        int 21h         ; wychodzimy
     _________________________________________________________________

   Kompilujemy go nastepujaco (wszystkie programy bedziemy tak
   kompilowac, chyba ze powiem inaczej):
        tasm flagi.asm
        tlink flagi.obj /t

   Lub, dla NASMa:
        nasm -o flagi.com -f bin flagi.asm

   Lub, dla FASMa:
        fasm flagi.asm flagi.com

   Nie ma w tym programie wielkiej filozofii. Po prostu 25 bajtow
   radosci...
   Dociekliwy zapyta, z jakim kodem wyjscia wychodzi ten program.
   Odpowiedz brzmi oczywiscie:
   - Albo 30h albo 31h, w zaleznosci od ostatniego bitu oryginalnych
   flag.
     _________________________________________________________________

   Teraz krotki programik, ktorego jedynym celem jest wyswietlenie na
   ekranie cyfr od 0 do 9, kazda w osobnej linii:
   (przeskocz program wyswietlajacy cyfry)
; tylko wersja NASM/FASM
;
; Program wypisuje na ekranie cyfry od 0 do 9
;
; kompilacja NASM:
;   nasm -O999 -o cyfry.com -f bin cyfry.asm
; kompilacja FASM:
;   fasm cyfry.asm cyfry.com

; definiujemy stale:

%define         lf      10      ; Line Feed
%define         cr      13      ; Carriage Return

; stale w wersji FASM:
;       lf = 10
;       cr = 13


org 100h                        ; robimy program typu .com

        mov     eax, 0          ; pierwsza wypisywana cyfra

wyswietlaj:
        call    _pisz_ld        ; uruchom procedure wyswietlania
                                ; liczby w EAX
        call    _nwln           ; uruchom procedure, ktora przechodzi
                                ; do nowej linii
        add     eax, 1          ; zwiekszamy cyfre
        cmp     eax, 10         ; sprawdzamy, czy ciagle EAX < 10
        jb      wyswietlaj      ; jesli EAX < 10, to wyswietlamy
                                ; cyfre na ekranie

        mov     ax, 4c00h       ; funkcja wyjscia z programu
        int     21h             ; wychodzimy



; nastepujace procedury (wyswietlanie liczby i przechodzenie
; do nowego wiersza) nie sa az tak istotne, aby omawiac je
; szczegolowo, gdyz w przyszlosci bedziemy uzywac tych samych
; procedur, ale z biblioteki, a te wstawilem tutaj dla
; uproszczenia kompilacji programu.

; Ogolny schemat dzialania tej procedury wyglada tak:
; wezmy liczbe EAX=12345. Robimy tak:
; 1. dzielimy EAX przez 10. reszta = EDX = DL = 5.
; Zapisz do bufora. EAX = 1234 (iloraz)
; 2. dzielimy EAX przez 10. reszta = DL = 4.
; Zapisz do bufora. EAX=123 (iloraz)
; 3. dzielimy EAX przez 10. reszta = DL = 3.
; Zapisz do bufora. EAX=12
; 4. dziel EAX przez 10. DL = 2. zapisz. iloraz = EAX = 1
; 5. dziel EAX przez 10. DL = 1. zapisz. iloraz = EAX = 0.
; Przerywamy petle.
; Teraz w buforze sa znaki: "54321". Wystarczy wypisac
; wspak i oryginalna liczba pojawia sie na ekranie.


_pisz_ld:

;pisz32e
;we: EAX=liczba bez znaku do wypisania

        pushfd                  ; zachowujemy modyfikowane rejestry
        push    ecx
        push    edx
        push    eax
        push    esi

        xor si,si               ; SI bedzie wskaznikiem do miejsca,
                                ; gdzie przechowujemy cyfry.
                                ; Teraz SI=0.

        mov ecx,10              ; liczba, przez ktora bedziemy dzielic

_pisz_ld_petla:
        xor edx,edx             ; wyzeruj EDX, bo instrukcja DIV
                                ; go uzywa
        div ecx                 ; dzielimy EAX przez 10

        mov [_pisz_bufor+si],dl ; do bufora ida reszty z dzielenia
                                ; przez 10, czyli cyfry wspak

        inc si                  ; zwieksz wskaznik na wolne miejsce.
                                ; Przy okazji, SI jest tez iloscia
                                ; cyfr w buforze

        or eax,eax              ; sprawdzamy, czy liczba =0
        jnz _pisz_ld_petla      ; jesli nie, to dalej ja dzielimy
                                ; przez 10

        mov ah,0eh              ; funkcja wypisywania
_pisz_ld_wypis:
        mov al,[_pisz_bufor+si-1]       ; wypisujemy reszty wspak
        or al,"0"       ; z wartosci 0-9 zrobimy cyfre "0"-"9"
        int 10h                 ; wypisujemy cyfre

        dec si                  ; przechodzimy na wczesniejsza cyfre
        jnz _pisz_ld_wypis      ; jesli SI=0, to nie ma juz cyfr

        pop esi                 ; przywracamy zmienione rejestry
        pop eax
        pop edx
        pop ecx
        popfd

        ret                     ; powrot z procedury

_pisz_bufor:    times   40      db 0    ; miejsce na 40 cyferek (bajtow)

_nwln:

;wyswietla znak konca linii (Windows)

        push ax
        push bp
        mov ax,(0eh << 8) | lf          ; AX = 0e0ah
        int 10h                         ; wyswietlamy znak LF
        mov al,cr
        int 10h                         ; wyswietlamy znak CR
        pop bp
        pop ax
        ret
     _________________________________________________________________

   Nastepny "twor" nie jest wolno stojacym programem, ale pewna
   procedura. Pobiera ona informacje z rejestru AL i wypisuje, co trzeba.
   Oto ona:
   (przeskocz procedure _pisz_ch)
_pisz_ch:

;we: AL=cyfra heksadecymalna do wypisania 0...15
; CF=1 jesli blad

        push bp         ; zachowaj modyfikowane rejestry: BP, AX, Flagi
        push ax
        pushf

        cmp al,9                ; Sprawdzamy dane wejsciowe : AL jest w
                                ; 0-9 czy w 10-15?
        ja _ch_hex              ; AL > 9. Skok do _ch_hex
        or al,30h               ; 0 < AL < 9. Or ustawia 2 bity,
                                ; czyniac z AL liczbe z
                                ; przedzialu 30h - 39h, czyli od "0"
                                ; do "9". Mozna bylo napisac
                                ; "ADD al,30h", ale zdecydowalem sie
                                ; na OR, bo jest szybsze a efekt ten sam.

        jmp short _ch_pz        ; AL juz poprawione. Skacz do miejsca,
                                ; gdzie wypisujemy znak.

_ch_hex:                        ; AL > 9. Moze bedzie to cyfra hex,
                                ; moze nie.
        cmp al,15               ; AL > 15?
        ja _blad_ch             ; jesli tak, to mamy blad
        add al,"A"-10   ; Duzy skok myslowy. Ale wystarczy to rozbic
                                ; na 2 kroki i wszystko staje sie jasne.
                                ; Najpierw odejmujemy 10 od AL. Zamiast
                                ; liczby od 10 do 15 mamy juz liczbe
                                ; od 0 do 5. Teraz te liczbe dodajemy do
                                ; "A", czyli kodu ASCII litery A,
                                ; otrzymujac znak od "A" do "F"

_ch_pz:                         ; miejsce wypisywania znakow.
        mov ah,0eh              ; numer funkcji: 0Eh
        int 10h                 ; wypisz znak

        popf                    ; zdejmij ze stosu flagi
        clc                     ; CF := 0 dla zaznaczenia braku bledu
                                ; (patrz opis procedury powyzej)
        jmp short _ch_ok        ; skok do wyjscia

_blad_ch:                       ; sekcja obslugi bledu (AL > 15)
        popf                    ; zdejmij ze stosu flagi
        stc                     ; CF := 1 na znak bledu

_ch_ok:                         ; miejsce wyjscia z procedury
        pop ax                  ; zdejmij modyfikowane rejestry
        pop bp

        ret                     ; return, powrot

   To chyba nie bylo zbyt trudne, co?
   Szczegoly dotyczace pisania procedur (i bibliotek) znajduja sie w moim
   artykule o pisaniu bibliotek.
     _________________________________________________________________

   Teraz pokaze pewien program, ktory wybralem ze wzgledu na duza liczbe
   roznych instrukcji i sztuczek. Niestety, nie jest on krotki. Ale
   wspolnie sprobujemy przez niego przejsc. Jest to wersja dla TASMa, ale
   obok instrukcji postaram sie zamiescic ich NASMowe odpowiedniki. Oto
   on:
   (przeskocz program zliczajacy liczby pierwsze)
; Program liczy liczby pierwsze w przedzialach
; 2-10, 2-100, 2-1000,... 2-100.000
;
; Autor: Bogdan D.
; kontakt: bogdandr (at) op (dot) pl
;
; kompilacja TASM:
;   tasm ile_pier.asm
;   tlink ile_pier.obj /t
;
; kompilacja NASM:
;   nasm -f bin -o ile_pier.com ile_pier.asm
;
; kompilacja FASM:
;   fasm ile_pier.asm ile_pier.com


.model tiny             ; to bedzie maly program. NASM/FASM: usunac.
.code                   ; poczatek segmentu kodu. NASM:
                        ; "section .text" lub nic. FASM: nic
.386                    ; bedziemy uzywac rejestrow 32-bitowych.
                        ; NASM: "CPU 386" lub nic, FASM: nic

org 100h                ; to bedzie program typu .com

start:                  ; poczatek...

        xor ebx,ebx     ; EBX = liczba, ktora sprawdzamy, czy jest
                        ; pierwsza. Zaczniemy od 3. Ponizej jest 3
                        ; razy INC (zwieksz o 1). Najpierw EBX = 0,
                        ; bo "XOR rej,rej" zeruje dany rejestr.

        xor edi,edi     ; EDI = biezacy licznik liczb pierwszych

        xor ecx,ecx     ; ECX = stary licznik liczb (z poprzedniego
                        ; przedzialu).
                        ; Chwilowo, oczywiscie 0.

        inc ebx         ; EBX = 1

        mov esi,10      ; ESI = biezacy koniec przedzialu: 10, 100, ..

        inc edi         ; EDI = 1. uwzgledniamy 2, ktora jest
                        ; liczba pierwsza

        inc ebx         ; EBX = 2, pierwsza liczba bedzie = 3

petla:                  ; petla przedzialu

        cmp ebx,esi     ; czy koniec przedzialu? (ebx=liczba,
                        ; esi=koniec przedzialu)
        jae pisz        ; EBX >= ESI - idz do sekcji wypisywania
                        ; wynikow

        mov ebp,2       ; EBP - liczby, przez ktore bedziemy dzielic.
                        ; pierwszy dzielnik = 2

        inc ebx         ; zwiekszamy liczbe. EBX=3. Bedzie to
                        ; pierwsza sprawdzana.

spr:                    ; petla sprawdzania pojedynczej liczby

        mov eax,ebx     ; EAX = sprawdzana liczba
        xor edx,edx     ; EDX = 0
        div ebp         ; EAX = EAX/EBP (EDX bylo=0),
                        ; EDX=reszta z dzielenia

        or edx,edx      ; instrukcja OR tak jak wiele innych,
                        ; ustawi flage zera ZF na 1, gdy jej wynik
                        ; byl zerem. W tym przypadku pytamy:
                        ; czy EDX jest zerem?

        jz petla        ; jezeli dzieli sie bez reszty (reszta=EDX=0),
                        ; to nie jest liczba pierwsza
                        ; i nalezy zwiekszyc liczbe sprawdzana
                        ; (inc ebx)

        inc ebp         ; zwiekszamy dzielnik

        cmp ebp,ebx     ; dzielniki az do liczby
        jb spr          ; liczba > dzielnik - sprawdzaj dalej te
                        ; liczbe. Wiem, ze mozna bylo sprawdzac tylko
                        ; do SQRT(liczba) lub LICZBA/2, ale
                        ; wydluzyloby to program i brakowalo mi juz
                        ; rejestrow...

juz:                    ; przerobilismy wszystkie dzielniki,
                        ; zawsze wychodzila reszta,
                        ; wiec liczba badana jest pierwsza

        inc edi         ; zwiekszamy licznik liczb znalezionych
        jmp short petla ; sprawdzaj kolejna liczbe az do konca
                        ; przedzialu.

                        ; sekcja wypisywania informacji

pisz:
        mov edx,offset przedzial        ; NASM/FASM: bez "offset"
        mov ah,9
        int 21h         ; wypisujemy napis "Przedzial 2-...."

        mov eax,esi     ; EAX=ESI=koniec przedzialu
        call _pisz_ld   ; wypisz ten koniec (EAX)


; NASM: mov ax,(0eh << 8) | ":" ; << to shift left, | to logiczne OR

        mov ax,(0eh shl 8) or ":" ; to wyglada zbyt skomplikowanie,
                        ; ale jest o dziwo prawidlowa instrukcja.
                        ; Jest tak dlatego, ze wyrazenie z prawej
                        ; strony jest obliczane przez kompilator.
                        ; 0eh przesuniete w lewo o 8 miejsc daje
                        ; 0E00 w AX. Dalej, dopisujemy do tego
                        ; dwukropek, ktorego kod ASCII nas nie
                        ; interesuje a bedzie obliczony przez
                        ; kompilator. Ostatecznie, to wyrazenie
                        ; zostanie skompilowane jako "mov ax,0e3a".
                        ; Chodzi o to po prostu, aby
                        ; nie uczyc sie tabeli kodow ASCII na pamiec.

        int 10h         ; wypisujemy dwukropek

        add ecx,edi     ; dodajemy poprzednia liczbe znalezionych
                        ; liczb pierwszych
        mov eax,ecx     ; EAX = liczba liczb pierwszych od 2 do
                        ; konca biezacego przedzialu

        call _pisz_ld   ; wypisujemy te liczbe.


        mov ah,1        ; int 16h, funkcja nr 1: czy w buforze
                        ; klawiatury jest znak?
        int 16h
        jz dalej        ; ZF = 1 oznacza brak znaku. Pracuj dalej.
        xor ah,ah
        int 16h         ; pobierz ten znak z bufora
                        ; (int 16h/ah=1 tego nie robi)
koniec:
        mov ax,4c00h
        int 21h         ; wyjdz z programu z kodem wyjscia = 0

dalej:                  ; nie nacisnieto klawisza
        cmp esi,100000  ; 10^5
        je koniec       ; ESI = 100.000? Tak - koniec, bo dalej
                        ; liczy zbyt dlugo.

        mov eax,esi     ; EAX=ESI
        shl eax,3       ; EAX = EAX*8
        shl esi,1       ; ESI=ESI*2
        add esi,eax     ; ESI = ESI*2 + EAX*8 =ESI*2+ESI*8= ESI*10.
                        ; Znacznie szybciej niz MUL

        xor edi,edi     ; biezacy licznik liczb

        jmp short petla ; robimy od poczatku...

przedzial       db      10,13,"Przedzial 2-$"


; NASM/FASM:
; _pisz_bufor: times 6 db 0
_pisz_bufor db 6 dup (0) ; miejsce na cyfry dla nastepujacej procedury:


_pisz_ld:

;we: EAX=liczba bez znaku do wypisania

        push ecx                ; zachowujemy modyfikowane rejestry
        push edx
        push eax
        push esi

        xor si,si       ; SI=0. Bedzie wskaznikiem w powyzszy bufor.

        mov ecx,10      ; bedziemy dzielic przez 10, aby uzyskiwac
                        ; kolejne cyfry. Reszty z dzielenia pojda
                        ; do bufora, potem beda wypisane wspak, bo
                        ; pierwsza reszta jest przeciez cyfra jednosci

_pisz_ld_petla:
        xor edx,edx     ; EDX=0

        div ecx         ; EAX=EAX/ECX, EDX = reszta, ktora miesci sie
                        ; w DL, bo to jest tylko 1 cyfra dziesietna

        mov [_pisz_bufor+si],dl ; Cyfra do bufora.

        inc si          ; Zwieksz numer komorki w buforze, do ktorej
                        ; bedziemy teraz pisac

        or eax,eax      ; EAX = 0 ?

        jnz _pisz_ld_petla ; Jesli nie (JNZ), to skok do poczatku petli

        mov ah,0eh              ; funkcja wypisania
_pisz_ld_wypis:
        mov al,[_pisz_bufor+si-1] ; SI wskazuje poza ostatnia cyfre,
                        ; dlatego jest -1. Teraz AL= ostatnia cyfra,
                        ; czyli ta najbardziej znaczaca w liczbie

                ; Zamien liczbe 0-9 w AL na gotowa do wypisania cyfre:
        or al,"0" ; lub "OR al,30h" lub "ADD al,30h".

        int 10h                 ; wypisz AL

        dec si                  ; zmniejsz wskaznik do bufora.

        jnz _pisz_ld_wypis      ; Jesli ten wskaznik (SI) nie jest zerem,
                                ; wypisuj dalej

        pop esi                 ; odzyskaj zachowane rejestry
        pop eax
        pop edx
        pop ecx

        ret                     ; powrot z procedury

end start                       ; NASM/FASM: usunac te linijke

   Kilka uwag o tym programie:
     * Czemu nie zrobilem MOV EBX,2 a potem INC EBX, ktore musialo byc w
       petli?
       Bo XOR EBX, EBX jest krotsze i szybsze.
     * Dobra. Wiec czemu nie:
                xor ebx,ebx
                inc ebx
                inc ebx
       Te instrukcje operuja na tym samym rejestrze i kazda musi
       poczekac, az poprzednia sie zakonczy. Wspolczesne procesory
       potrafia wykonywac niezalezne czynnosci rownolegle, dlatego
       wcisnalem w srodek jeszcze kilka niezaleznych instrukcji.
     * Ten program sprawdza za duzo dzielnikow. Nie mozna bylo sprawic,
       by sprawdzal tylko do na przyklad polowy sprawdzanej liczby?
       Mozna bylo. Uzywajac zmiennych w pamieci. Niechetnie to robie, bo
       w porownaniu z predkoscia operacji procesora, pamiec jest wprost
       NIEWIARYGODNIE wolna. Zalezalo mi na szybkosci.
     * Czy zamiast
                mov ax,(0eh shl 8) or ":"
       nie prosciej byloby zapisac
                mov ah,0eh
                mov al,":"      ; lub 3ah
       Jasne, ze byloby prosciej... zrozumiec. Ale nie wykonac dla
       procesora. Jedna instrukcje wykonuje sie szybciej niz 2 i to
       jeszcze posrednio operujace na tym samym rejestrze (AX).
     * Czy nie prosciej zamiast tych wszystkich SHL zapisac jedno MUL lub
       IMUL?
       Jasne, ze prosciej. Przy okazji dobre kilka[nascie] razy wolniej.
     * Dlaczego ciagle XOR rej,rej?
       Szybsze niz MOV rej,0, gdzie to zero musi byc czesto zapisane 4
       bajtami zerowymi. Tak wiec i krotsze. Oprocz tego, dzieki
       instrukcji XOR lub SUB wykonanej na tym samym rejestrze, procesor
       wie, ze ten rejestr juz jest pusty. Moze to przyspieszyc niektore
       operacje.
     * Dlaczego na niektorych etykietach sa jakies znaki podkreslenia z
       przodu?
       Niektore procedury sa zywcem wyjete z mojej biblioteki, piszac
       ktora musialem zadbac, by przypadkowo nazwa jakies mojej procedury
       nie byla identyczna z nazwa jakiejs innej napisanej w programie
       korzystajacym z biblioteki.
       Czy nie moglem tego potem zmienic?
       Jasne, ze moglem. Ale nie bylo takiej potrzeby.
     * Czemu OR rej,rej a nie CMP rej,0?
       OR jest krotsze i szybsze. Mozna tez uzywac TEST rej,rej, ktore
       nie zmienia zawartosci rejestru.
     * Czemu OR al, "0"?
       Bardziej czytelne niz ADD/OR al,30h. Chodzi o to, aby dodac kod
       ASCII zera. I mozna to zrobic bardziej lub mniej czytelnie.

   Wiem, ze ten program nie jest doskonaly. Ale taki juz po prostu
   napisalem...
   Nie martwcie sie, jesli czegos od razu nie zrozumiecie. Naprawde, z
   czasem samo przyjdzie. Ja tez przeciez nie umialem wszystkiego od
   razu.

   Inny program do liczb pierwszych znajdziecie tu: prime.txt.

   Nastepnym razem cos o ulamkach i koprocesorze.

     Podstawowe prawo logiki:
     Jezeli wiesz, ze nic nie wiesz, to nic nie wiesz.
     Jezeli wiesz, ze nic nie wiesz, to cos wiesz.
     Wiec nie wiesz, ze nic nie wiesz.

   Poprzednia czesc kursu (klawisz dostepu 3)
   Kolejna czesc kursu (klawisz dostepu 4)
   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
     _________________________________________________________________

Cwiczenia

   (mozna korzystac z zamieszczonych tu procedur)
    1. Napisz program, ktory na ekranie wyswietli liczby od 90 do 100.
    2. Napisz program sprawdzajacy, czy dana liczba (umiescisz ja w
       kodzie, nie musi byc wczytywana znikad) jest liczba pierwsza.
    3. Napisz program wypisujacy dzielniki danej liczby (liczba tez w
       kodzie).
