Inštrukcie pre prácu s reťazcami |
||
| <= | => | |
ASM86 má veľmi silný nástroj v reťazcových inštrukciách. Za reťazec je tu na rozdiel od Pascalovského považovaný blok dát v pamäti o skoro ľubovolnej dĺžke (podľa definície sme obmedzení len veľkosťou segmentu, to sa ale dá jednoducho obísť). Pre použitie reťazcových inštrukcií sú vyčlenené dvojice registrov, ktoré nesú adresy:
Nasledujúci príklad využíva priamy zápis do videopamäti (VRAM) v textovom režime VGA k výstupu reťazca. VRAM, začína na adrese 0xB8000. Je organizovaná ako pole slov nesúce informácie o zobrazovaných znakoch. Každé slovo nesie slabiku atribútov (farba znaku a jeho pozadie) a slabiku s ASCII kódom zobrazeného znaku. 80 slov VRAM je jeden riadok na obrazovke. Preto pri zvýšení adresy 0xB8000:0x0000 o 160 môžeme pracovať s druhým riadkom atd.
#include <conio.h>
#include <string.h>
char slovo[254]="Ahoj"; // dĺžka reťazca na začiatku
unsigned char dlzka;
void main() {
dlzka = strlen(slovo);
clrscr();
asm {
PUSH DS // ulož obsah DS do zásobníka, budeme ho meniť
JMP dal // obídi dáta
label vram dword
DW 0x0000,0xB800 // offset:segment VRAM, Pozor! je to obrátene
label adsl dword
DD slovo // adresa slova, ukazovateľ na neho
}
dal: // začiatok programu
asm {
LDS SI, CS:[OFFSET adsl] // DS:SI nasmeruj na zdroj (na slovo)
LES DI, CS:[OFFSET vram] // ES:DI nesmeruj na VRAM
XOR CH,CH // nuluj CH
MOV CL,dlzka // do CL daj dĺžku reťazca slovo, 1. slabiku
MOV AH,0x6F // do AH daj atribúty nápisu
}
cyk: // cyklus pre znak po znaku
asm {
LODSB // naber kód znaku z reťazca do AL a zvýš SI+1
STOSW // ulož obsah AX do VRAM, zvýš DI+2
LOOP cyk // zníž CX o jednu, ak nie je nula choď na cyk
POP DS // vráť register DS do pôvodného stavu
}
}
Uvedený program zmení slabiku na slovo v registru AX s tým, že bude kód znaku doplnený o atribúty. Ak zmeníme hodnotu v AH ovplyvníme tým farbu výstupu.
#include <stdio.h>
char slovo1[254]=”Ahoj”, slovo2[254];
unsigned char dlzka;
main() {
dlzka = strlen(slovo1);
asm {
PUSH DS // ulož do zásobníka obsah DS, zmeníme ho
JMP dal // skoč na začiatok, obídi dáta
label adr dword
DD slovo1,slovo2 // definícia ukazovateľov na pole
}
dal:
asm {
LDS SI,CS:[OFFSET adr] // naber adresu zdrojového reťazca
LES DI,CS:[OFFSET adr+4] // naber adresu cieľového reťazca
XOR CH,CH // nuluj CH
MOV CL,dlzka // do CL daj dĺžku reťazca
REP MOVSB // kopíruj reťazce po slabikách
POP DS // vráť obsah DS zo zásobníka
}
printf(“\n%s %s”, slovo1, slovo2);
getchar();
}
V príklade kopírujeme jen toľko prvkov, koľko má zdrojové slovo slabík. Túto informáciu si zistíme funkciou strlen. Aj keď všetky presuny sa odohrávajú v dátovom segmente s adresou v DS, je dobré si zvyknúť na to, že vždy, keď meníme DS, ukladáme jeho obsah pre istotu do zásobníka.
Reťacové inštrukcie vyhľadania a porovnania využívajú register príznakov ZF. Preto ASM86 obsahuje naviac prefixy podmieneného opakovania:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
unsigned int pole[10];
unsigned int hladany, pozicia;
unsigned char i;
void main() {
clrscr();
randomize();
for (i=0; i<=9;i++)
pole[i]=random(32767); // do poľa náhodné čísla
hladany=pole[random(10)]; // vyber hľadané číslo
printf("Hladam: %d",hladany);
asm {
JMP zac // skok na začiatok
label adr dword
DD pole // definícia ukazovateľa na pole
}
zac:
asm {
MOV AX, hladany // do AX vlož hľadané číslo
MOV CX, 10 // do CX vlož dĺžku reťazca (pole)
LES DI, CS:[OFFSET adr] // naber adresu reťazca
REPNE SCASW // opakuj do zhody porovnania
MOV pozicia, 9 // spočítaj koľký je hľadaný,
SUB pozicia,CX // k tomu použiješ to, čo zostalo v CX
}
for (i=0; i<=9; i++) {
if (i!=pozicia)
textcolor(15);
else
textcolor(12);
cprintf("\n\r%d", pole[i]);
}
getch();
}
Tento program vyhľadá slovo v poli. K tomu slúži len riadok REPNE SCASW. Ten opakuje pohyb po poli, dokiaľ nenájde zhodu s hodnotou v registri AX (tá sa prejaví nastavením ZF do 1) . K zisteniu pozície hľadaného dobre poslúži zvyšok v registri CX. Keby bol zvyšok nulový, hľadaný prvok by v poli nebol.
#include <string.h>
#include <conio.h>
char slovo1[254]="Nazdar programatori! Skuste vyhladat nejake slovo z tejto vety.";
char slovo2[254]="slovo";
unsigned int i, miesto, dlzka1, dlzka2;
void main () {
dlzka2 = strlen(slovo2);
dlzka1 = strlen(slovo1);
asm {
PUSH DS // ulož DS, budeme ho meniť
JMP dal // preskoč dáta
label ukp dword
DD slovo1, slovo2 // ukazovatele na reťazce
}
dal:
asm LDS SI,CS:[OFFSET ukp] // naber adresu zdroja
cyk:
asm {
LES DI,CS:[OFFSET ukp+4] // naber adresu cieľa, hľadaného slova
MOV CX, dlzka2 // do CX vlož dĺžku reťazca
REPE CMPSB // opakuj do nezhody (konca hľadaného)
JZ koniec // ak bola zhoda tak na koniec
SUB SI, dlzka2 // ak ale bola zhoda tak sa v SI vráť
INC SI
ADD SI,CX // pre návratu v SI použi zvyšok v CX
JMP cyk // a znovu hľadať
}
koniec:
asm {
POP DS // vráť obsah DS, už ho nebudeme meniť
MOV miesto,SI // vypočítaj miesto v prehľadávanom
MOV SI, word ptr cs:[OFFSET ukp] // k tomu použiješ dĺžku reťazca zdroja
ADD SI, dlzka2 // dĺžku cieľa, teda hľadaného
SUB miesto,SI
}
clrscr();
for (i=0; i<=dlzka1; i++) {
if (!((i>=miesto) && (i<=miesto+dlzka2-1)))
textcolor(15);
else
textcolor(12);
cprintf("%c",slovo1[i]);
}
getch();
}
V príklade prehľadávame
reťazec slovo1. Hľadáme v ňom umiestnenie podreťazca slovo2. Program má
dva cykly v sebe. Prvý zaisťuje pohyb po prehľadávanom reťazci v prípade
nezhody (je realizovaný JMP). Druhý vnútorný zaisťuje pohyb po prehľadávanom
s kontrolou s hľadaným (je realizovaný REPE). V prípade zhody je
po cyklu REPE v registru ZF = 1 (proste nevyskočil nezhodou ale
nulou v CX => koniec hľadaného slova a zhoda). Preto cyklus prehľadávania
ukončíme podmieneným skokom JZ na koniec. Tu sa zistí adresa v prehľadávanom
reťazci. To je ale adresa za posledným znakom zhody. Preto sa vrátime naspäť
o dĺžku slova (tam je hľadané slovo).
| <= | => |