Sunteți pe pagina 1din 4

Limbaje de asamblare – laborator 2

Deoarece invocarea asamblorului urmată de linkeditor și apoi dacă este în regulă de


programul în sine este greoaie, vom recurge la un pic de programare în limbajul de comandă
DOS. Este un limbaj extrem de primitiv, dar poate fi eficient. Fișierul ASM.BAT poate fi plasat
undeva în PATH, de ex. in C:\NC. Fișierele intermediare cu extensiile .MAP și .OBJ vor fi
șterse indiferent de rezultat.

@echo off
c:\borlandc\bin\tasm.exe %1
if errorlevel 1 goto err
c:\borlandc\bin\tlink.exe %1
if errorlevel 1 goto err

echo Running program...


%1
goto quit

:err
echo Assembler error!
:quit
del %1.MAP >NUL
del %1.OBJ >NUL

Acum pentru a asambla și rula un program se poate folosi comanda ASM PROG unde
PROG este numele fișierului PROG.ASM fără extensie.

Vom fi nevoiți să anticipăm anumite noțiuni, fără de care nu se poate face aproape
nimic. De ex. este imposibil de conceput programare fără decizii, care în Assembler sunt sub
forma de salturi condiționate. De asemenea, dată fiind complexitatea codului la care se poate
ajunge este neapărat nevoie de subrutine/proceduri.
În această lucrare vom analiza problema afișării unor numere în hexazecimal. Începem
cu niblul inferior (biții 0-3) al unui registru, alegem DL pentru că este convenabil. Avem funcția
DOS 02h care afișează caracterul al cărui cod ASCII este în DL de aici.
Pentru a păstra doar niblul inferior trebuie să aplicăm o mască de biți și operația logică
bit-cu-bit AND, masca fiind 0Fh = 00001111b. Apoi va trebui să transformăm numărul din DL
într-un cod ASCII, dar cifrele și literele A-F nu au coduri consecutive deci este nevoie de cel
puțin o decizie.

model small
.stack 1024
.data
.code
start:
mov ax, @data
mov ds, ax

mov dl, 13 ; numarul de afisat

and dl, 0Fh ; lasa numai niblul inferior


cmp dl, 0Ah ; comparatie cu 10
jb et1 ; salt daca AL<10
add dl, 'A'-10 ; 'A'-10 este o expresie constanta
jmp et2
et1:
add dl, '0' ; transformam numarul in cod ASCII
et2:
mov ah, 02h ; apel functie DOS 02h, afiseaza char in DL
int 21h

mov ah, 02h ; print CR (carriage return)


mov dl, 0Dh
int 21h
mov ah, 02h ; print LF (line feed)
mov dl, 0Ah
int 21h

mov ax, 4c00h


int 21h
end start

Salturile necondiționate au mnemonica JMP, cele condiționate în afară de JCXZ


urmează o convenție de numire:
- litera J (Jump if)
- opțional litera N (Not)
- fie prima literă a unui indicator de condiție (C, Z, O, S, P) fie o condiție aritmetică
- în caz că a fost condiție aritmetică, opțional E (or Equal)
Condiția aritmetică indică dacă se face saltul în urma unei instrucțiuni CMP. Aceasta
este identică cu SUB (scădere) dar nu dă nici un rezultat, doar modifică flagurile. Poate fi una
din literele B, A, L, G adică Below/Above (numere fără semn) respectiv Less/Greater (numere
cu semn). Lista de salturi condiționate este aici.
Decizia luată în exemplul de mai sus este una de tip if-then-else. În C am putea scrie:
if (DL >= 10) DL += 'A' – 10
else DL += '0';
unde codurile ASCII sunt 65 pentru 'A' respectiv 48 pentru '0'. Am putea deci scrie și așa,
eliminând astfel unul din salturi:
if (DL >= 10) DL += 'A' - '0' - 10;
DL += '0';
OBS: Folosirea expresiilor constante poate face programul mai lizibil. Expresia
'A'-'0'-10 are o semnificație în timp ce valoarea ei – adică 7 – nu înseamnă nimic. Inspectând
codul cu un debugger (ca TD) vom vedea că expresia constantă este înlocuită în cod de valoarea
ei.
Înainte de terminarea programului avem nevoie să afișăm și un newline, adică în
DOS/Windows combinația CR+LF, codurile 0Dh și 0Ah.
Dar aceste secvențe de cod pot fi folosite repetat într-un program, ceea ce face necesară
folosirea procedurilor. Deoarece acestea sunt proceduri de I/O și deci viteza nu este critică vom
prefera să salvăm valorile regiștrilor la intrarea în procedură și să le restaurăm la ieșire, chiar
dacă acest lucru nu este absolut necesar sau se poate face înainte/după apelul procedurii (în
afara ei) dacă este.

model small
.stack 1024
.data
.code
PrintNewline PROC
push ax ; salvam registrii AX, DX
push dx
mov ah, 02h ; print CR (carriage return)
mov dl, 0Dh
int 21h
mov ah, 02h ; print LF (line feed)
mov dl, 0Ah
int 21h
pop dx ; restauram DX, AX
pop ax
ret
PrintNewline ENDP
PrintNibbleHex PROC
push ax ; salvam registrii AX, DX
push dx
mov dl, al
and dl, 0Fh ; lasa numai niblul inferior
cmp dl, 0Ah ; comparatie cu 10
jb PrintNibbleHex_et1 ; salt daca AL<10
add dl, 'A'-'0'-10 ; 'A'-'0'-10 este o expresie constanta
PrintNibbleHex_et1:
add dl, '0' ; transformam numarul in cod ASCII
mov ah, 02h ; apel functie DOS 02h, afiseaza char in DL
int 21h
pop dx ; restauram DX, AX
pop ax
ret
PrintNibbleHex ENDP

start:
mov ax, @data
mov ds, ax

mov al, 12 ; numarul de afisat

call PrintNibbleHex
call PrintNewline

mov ax, 4c00h


int 21h
end start

Procedura PrintNibbleHex primește numărul de afișat în niblul inferior al registrului


AL și nu modifică nici un registru deoarece AX și DX sunt salvați în stivă și restaurați ulterior.
Numai regiștrii pe 16 biți pot fi salvați/restaurați cu PUSH și POP, nu și cei pe 8.
În fine, putem merge în continuare cu proceduri pentru un octet (AL) respectiv un
cuvânt (AX):

model small
.stack 1024
.data
.code
PrintNewline PROC
push ax ; salvam registrii AX, DX
push dx
mov ah, 02h ; print CR (carriage return)
mov dl, 0Dh
int 21h
mov ah, 02h ; print LF (line feed)
mov dl, 0Ah
int 21h
pop dx ; restauram DX, AX
pop ax
ret
PrintNewline ENDP
PrintNibbleHex PROC
push ax ; salvam registrii AX, DX
push dx
mov dl, al
and dl, 0Fh ; lasa numai niblul inferior
cmp dl, 0Ah ; comparatie cu 10
jb PrintNibbleHex_et1 ; salt daca AL<10
add dl, 'A'-'0'-10 ; 'A'-'0'-10 este o expresie constanta
PrintNibbleHex_et1:
add dl, '0' ; transformam numarul in cod ASCII
mov ah, 02h ; apel functie DOS 02h, afiseaza char in DL
int 21h
pop dx ; restauram DX, AX
pop ax
ret
PrintNibbleHex ENDP
PrintByteHex PROC
push ax ; salvam AX (deci si AL)
shr al, 4 ; deplasare dreapta cu 4 biti
call PrintNibbleHex
pop ax ; restauram AX (deci si AL)
call PrintNibbleHex
ret
PrintByteHex ENDP
PrintWordHex PROC
xchg al, ah ; interschimb AL <-> AH
call PrintByteHex
xchg al, ah ; interschimb AL <-> AH
call PrintByteHex
ret
PrintWordHex ENDP

start:
mov ax, @data
mov ds, ax

mov ax, 0ABCDh ; numarul de afisat


call PrintWordHex
call PrintNewline

mov ax, 4c00h


int 21h
end start

OBS: Astfel de lanțuri de proceduri tind să devină foarte mari mergând mai departe.
Există însă posibilitatea de a separa fișierele odată ce procedurile au fost testate și merg.
Directiva include poate face asta.
OBS: Atenție la numele de fișiere! Ele trebuie să respecte convenția 8.3 altfel DOSBox
nu le vede și nu le poate accesa. Adică maxim 8 caractere în nume, punct și maxim 3 caractere
în extensie.

S-ar putea să vă placă și