Assembler pro blbé 3.díl

                           Petr Klimovič l.p.1996

            Jak  jsem  minule  slíbil,  zkusíme  si  vyrobit  program  pro
      konverzi češtiny. První návrh vypadal asi takto:

      MODEL SMALL
      .STACK 200
      .DATA
         FTabulka  db 'TABULKA.TAB',0       ;nazev prev. tabulky
         FVstup    db 'VSTUP.TXT',0         ;nazev vstupniho fajlu
         FVystup   db 'VYSTUP.TXT',0        ;nazev vystupniho fajlu
         FH2       dw 0                     ;filehandle vstupniho
         FH2       dw 0                     ;filehandle vystupniho
         MTabulka  db 256 DUP (0)           ;sem se nacte tabulka
         Buffer    db 30000 DUP (0)         ;buffer pro nacitana data
      .CODE
      START:
          Tabulka                 ;Nactu prevodni tabulku
          Otevri                  ;Otevru zdroj
          Vytvor                  ;Vytvorim cil
      Smycka:
          Nacti                   ;Nactu do bufferu kus textu
          Preved                  ;Prevedu jej
          Zapis                   ;Zapisu prevedeny text do fajlu
          jne Smycka              ;a furt dokola, cely soubor
          Zavri1                  ;Zavru zdrojovy
          Zavri2                  ;Zavru cilovy
          mov ax, 4c00h           ;Konec programu, navrat do DOSu
          int 21h
      END START

            Je  to  vlastně  hrubý  návrh. Vypadá to, že by to takto mohlo
      fungovat,  teď  zbývá  "jenom"  naprogramovat  jednotlivé procedury.

            Procedura  je  kus  kódu  začínající  příkazem PROC. Za ním je
      uveden   název  procedury,  pak  následují  normální  assemblerovské
      příkazy a celé to končí příkazem ENDP a za ním opět název procedury.

            Takže  třeba procedura, která přičte jedničku k AX (mimochodem
      pěkná kravina).

      PROC Pricti
         inc ax
         ret
      ENDP Pricti

            Potom  můžete  uvést  v  hlavním  programu příkaz call Přičti,
      který   způsobí  zvýšení  ax  o  jedničku.  Příkaz  call  je  volání
      procedury.

            Důležitá je instrukce ret, které způsobí návrat z procedury.

            Ještě bych měl vysvětlit co dělá DUP.

            MTabulka db 256 DUP (0)
            způsobí, že proměnná MTabulka bude zpočátku obsahovat 256 nul.

            Začneme  výrobou  procedury  Tabulka,  ta má za úkol načíst ze
      souboru  převodní tabulku do paměti. Takže otevření souboru, načtení
      256 bajtů (to je celá tabulka) a uzavření souboru.

      PROC Tabulka
         mov   ax,   3D00h            ;Otevreni souboru, read only
         mov   dx,   offset FTabulka  ;DS:DX ukazuje na nazev fajlu
         int   21h

         mov   bx,   ax               ;filehandle do bx
         push  bx                     ;uschovat na pozdeji
         mov   ah,   3Fh              ;cteni souboru
         mov   cx,   0FFh             ;FF bajtu (256)
         mov   dx,   offset MTabulka  ;DS:DX kam nacist
         int   21

         pop   bx                     ;vyndat uschovane filehandle
         mov   ah,   3Eh              ;zavreni souboru
         int   21
         ret
      ENDP Tabulka

            Další je Otevri a Vytvor, mělo by to být lehké.

      PROC Otevri
         mov   ax,   3D00h            ;Otevreni souboru, read only
         mov   dx,   offset FVstup    ;DS:DX ukazuje na nazev fajlu
         int   21h
         mov   FH1,   ax              ;schovat filehandle
         ret
      ENDP Otevri

      PROC Vytvor
        mov   ah,   3Ch               ;Vytvoreni souboru
        mov   cl,   0                 ;Zadny specialni atribut
        mov   ds    offset FVystup    ;DS:DX jmeno vyrabeneho fajlu
        int   21h
        mov   FH2,  ax                ;Schovat filehandle
        ret
      ENDP Vytvor

            Procedura  Nacti  by  měla  vždy  načíst  do  bufferu bajty ze
      vstupního souboru. Když jich načtu méně, než jsem zadal, znamená to,
      že jsem na konci souboru.

      PROC Nacti
         mov   bx,   FH1              ;filehandle do bx
         mov   ah,   3Fh              ;cteni souboru
         mov   cx,   30000            ;30000 bajtu, cely buffer
         mov   dx,   offset Buffer    ;DS:DX kam nacist
         int   21
         push ax                      ;ulozit kolik bajtu bylo nacteno
         ret
      ENDP Nacti

            Další procedura  převádí podle tabulky. Tabulka je vlastně 256
      čísel.  Jestliže  má  třeba  AL hodnotu 15, najde se v tabulce 15-tý
      údaj  a  ten  se  uloží  do AL. Takže tabulka, která neprovádí žádný
      převod, bude obsahovat postupku 0,1,2,3,4,5,6,7,8,9,10,11,....255.

      PROC Preved
         cld                            ;Ukazatele se budou zvysovat
         mov   bx,   offset MTabulka    ;Ukazatel do tabulky
         mov   si,   offset Buffer      ;Ukazatel do bufferu (cteni)
         mov   di,   si                 ;Ukazatel do bufferu (zapis)
         mov   cx,   ax                 ;Kolik bajtu budu prevadet
         xor   ah,   ah                 ;Znuluju AH
      Kolco:
         push bx                        ;Schovam si zacatek tabulky
         lodsb                          ;AL = [DS:SI]
         add   bx,   ax                 ;Posunu se v tabulce
         mov   al,   [ds:bx]            ;Prevedu
         stosb                          ;Zapisu zpatky do bufferu
         pop   bx                       ;Zase ukazuji na zacatek tab.
         loop Kolco                     ;a dalsi bajt bafru
         ret
      ENDP Preved

            Další  věc  je  opět  jednoduchá, zápis převedeného bufferu do
      fajlu.

      PROC Zapis
         mov   ah,   40h                ;Zapis fajlu
         mov   bx,   FH2                ;filehandle
         pop   cx                       ;kolik bajtu zapsat
         push  cx                       ;jeste se to bude hodit
         mov   dx,   offset Buffer      ;Kde jsou zapisovana data
         int   21h
         ret
      ENDP Zapis

            Takže  to  sepíšu dohromady a vykašlu se na procedury, jelikož
      žádnou  z  nich  nevykonávám  víckrát,  nemají  smysl  a navíc ještě
      přidávají ret, což je malilinkatá ztráta času.

                              Celkový zdroják 

            Většinu těchto instrukcí znáte, zbývá vysvětlit tři nové:

      LODSB
            Naplní  AL  hodnotou, kterou vezme z [DS:SI], potom posune SI,
      tak,  aby  ukazoval  na  další  bajt.  Buďto jej zvýší, nebo sníží o
      jedničku.   Jestli  se  bude  zvyšovat,  nebo  snižovat,  záleží  na
      nastavení   DF  (Direction  Flag),  DF  je  příznak,  kterým  směrem
      probíhají takovéto operace.

      STOSB
            Obrácená  instrukce  k  LODSB.  Uloží  AL  do paměti na adresu
      určenou  ES:DI a poté opět upraví ukazatel (v tomto případě DI), buď
      jej zvýší, nebo sníží.

      CLD
            Tohle  je  právě  to, co  určuje,  jestli  se  ukazatele budou
      zvyšovat,  nebo  snižovat.  Tímto příkazem nastavíte DF  tak, aby se
      ukazatel zvyšoval. Pro nastavení na snižování je instrukce STD.

       Aha, jak se tak dívám, tak ono je tu novějších věcí víc, takže:

                                  ZÁSOBNÍK     


            výheň