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ň