Lucrarea nr. 1
 

Programarea în limbajul de asamblare al procesoarelor x86

 
Definirea constantelor în limbaj de asamblare
Definirea datelor (variabilelor) în limbaj de asamblare
Declararea și utilizarea segmentelor în limbaj de asamblare
Definirea și utilizarea procedurilor în limbaj de asamblare
Formatul instrucțiunilor în limbaj de asamblare
Structura unui program în limbaj de asamblare
Asamblarea și editarea legăturilor

Registrele microprocesorul 8086
Setul de instrucțiuni al lui 8086

Tema de laborator
 
 

Definirea constantelor în limbaj de asamblare

În limbaj de asamblare o constantă se poate defini astfel:
nume EQU <valoare>
unde:
nume - numele constantei. Pentru o constantă nu se rezervă spațiu în memorie.
<valoare> - reprezintă valoarea ce este atribuită constantei.
Exemple:
PI              equ     3.14159
INT_DOS         equ     21h
BASE_ADDR       equ     378h


Definirea datelor (variabilelor) în limbaj de asamblare

Forma generală pentru definirea datelor este:
[nume] <tip> <lista_expresii> [;<comentariu>]
sau
[nume] <tip> <factor> DUP(<lista_expresii>) [;<comentariu>]
unde:
[ ] - indică faptul că acest câmp poate lipsi;
[nume] - numele prin care va fi referită data. Acest nume are asociat un tip și o valoare. Tipul rezultă din tipul datei iar valoarea este adresa la care se va găsi în memorie în timpul execuției programului primul octet rezervat pentru data etichetată cu numele respectiv.
<lista_expresii> - reprezintă lista unor expresii cu ale căror valori se vor inițializa zonele de date rezervate pentru declarația respectivă. Prin ‘?’ se specifică faptul că zona respectivă de date nu este inițializată.
<factor> - este un număr care indică de câte ori se repetă lista de expresii care urmează în paranteză.
<tip> - tipul de date:
DB  - date de tip octet (BYTE) - 8 biți;
DW - date de tip cuvânt sau adresă (WORD) - 16 biți;
DD  - date de tip pointer (DWORD) - 32 biți;
DQ  - date de tip constantă reală (QWORD) - 8 octeți;
DT  - date de tip constantă BCD (TBYTE) - 10 octeți.
Exemple:
x        dw    0
y        dw    ?
sir      db    'Sir de caractere!'
ptr      dd    1000h

 

Declararea și utilizarea segmentelor în limbaj de asamblare

Declararea unui segment se face sub forma:
<nume> SEGMENT [<tip_aliniere>] [<tip>] [<clasa>]
<corpul_segmentului>
<nume> ENDS
unde:
<nume> - este numele asociat segmentului. Acestui nume i se asociază o valoare și anume adresa de segment corespunzătoare poziției segmentului în memorie în faza de execuție a programului. Încărcarea unui registru segment cu o adresă de segment se face prin următoarea secvență de instrucțiuni:
MOV AX, <nume>
MOV DS, AX
<tip_aliniere> - poate să fie PARA, BYTE, WORD, PAGE și indică tipul adresei de început a segmentului, adică adresa de început a zonei de memorie rezervată segmentului este divizibilă cu 16, 1, 2, respectiv 256.
<tip> - este o informație pentru editorul de legături. Această informație indică raportul între acest segment și segmente definite în alte module obiect.
Având în vedere că într-un program pot să existe mai multe segmente este necesar ca asamblorul să cunoască în fiecare moment care sunt cele patru segmente logice curente pentru a putea să genereze corect codurile instrucțiunilor. De asemenea asamblorul trebuie să știe ce registru de segment se poate utiliza pentru a realiza accesul la datele referite. Această informație este transmisă asamblorului cu ajutorul pseudoinstrucțiunii ASSUME, care are următoarea forma genearală:
ASSUME {<registru_segment> : <segment>}
Definirea simplificată a segmentelor: acest tip de declarare a segmentelor este mai simplu de utilizat. Pentru a utiliza acest mod de definire trebuie sa se stabilească întâi care este modelul de memorie pentru care este scris programul respectiv:
.MODEL <tip_model>
unde <tip_model> poate fi: TINY, SMALL, MEDIUM, COMPACT, LARGE sau HUGE.

După stabilirea modelului pot apare următoarele pseudoinstrucțiuni:

.STACK [dimensiune] - segmentul de stivă;
.CODE [nume] - segmentul de cod;
.DATA - segmentul de date;
.DATA? - segment de date neinițializate;
.FARDATA [nume] - segment de date la care referirile se fac prin intermediul unor adrese complete (segment și offset);
.FARDATA? [nume]
.CONST - segment de date inițializate, date ale căror valori nu se modifică prin execuția programului.


Definirea și utilizarea procedurilor în limbaj de asamblare

Forma generală de definire a procedurilor este:
<nume> PROC [<atribut>]
<corp_procedură>
<nume> ENDP
unde:
<nume> - numele prin care se vor face apelurile procedurei. Acest nume are asociată ca valoare adresa primei instrucțiuni din procedură;
<atribut> - FAR sau NEAR.


Formatul instrucțiunilor în limbaj de asamblare

În limbaj de asamblare o instrucțiune se poate reprezenta pe o linie care are maximum 128 de caractere și care are forma generală:
[<eticheta>:] [<operație> [<operanzi>]]  [;<comentariu>]
unde:
<eticheta> - un nume care are asociată adresa relativă în cadrul segmentului din care face parte primul octet din instrucțiunea etichetată;
<operație> - mnemonica corespunzătoare instrucțiunii;
<operanzi> - este un câmp a cărui existență și formă depind de tipul instrucțiunii.


Structura unui program în limbaj de asamblare

 
;;
.286                                ; se acceptă instrucțiuni de 286
INT_DOS equ 21h                     ; definirea unei constante

data_seg segment                    ; segmentul de date
hex_digit db '0123456789ABCDEF'
data_seg ends

bios_seg segment at 0000h           ; un alt segment de date care se
                                    ; suprapune peste zona de date BIOS
bios_seg ends

stack_seg segment stack             ; segmentul de stivă
dw 100h dup (?)
top_off label word
stack_seg ends

code_seg segment 'code'             ; segmentul de cod
    assume cs:code_seg,ds:data_seg,es:data_seg,ss:stack_seg

; definirea procedurilor
nume_proc proc near
; input :
; output:
          ret
nume_proc endp

; programul principal
start:
            mov ax,data_seg         ; inițializarea segmentului de
            mov ds,ax               ; date
            mov es,ax               ;
            mov ax,stack_seg        ; inițializarea segmentului de
            mov ss,ax               ; stivă
            mov sp,offset top_off   ;

            ...                     ; aici se plasează programul

end_prog:
            mov ax,4c00h            ; exit to DOS
            int INT_DOS             ;
code_seg ends
end start


Asamblarea și editarea legăturilor

Asamblarea unui program în limbaj de asamblare

    tasm nume_fisier.asm ;

sau

    masm nume_fisier.asm

Iar editarea legăturilor:

    tlink nume_fisier.obj ;

sau

    link nume_fisier.obj
 


Registrele microprocesorul 8086

Pot fi clasificate din punctul de vedere al rolului pe care îl au în 5 grupuri: Registrele generale (AX, BX, CX, DX) - sunt utilizate în instrucțiunile aritmetice și logice. Majoritatea instrucțiunilor aritmetice utilizează în același mod toate registrele. Există însă unele instrucțiuni aritmetice pentru care anumite registre generale au semnificații speciale: AX - registru acumulator; BX - registru bază; CX - registru contor; DX - utilizat implicit în operațiile de înmulțire și împărțire; și de asemenea poate conține adresa unui port. Toate cele patru registre generale pot fi utilizate și ca perechi de registre pe 8 biți: AX - AH, AL.


Figura 2.1 Registrele generale ale procesorului 8086

Registrele indicatoare de adresă (SP, BP) - sunt registre care conțin adrese relative în segmentul stivă curent. Registrul SP are semnificația de adresă curentă a vârfului stivei, iar registrul BP este utilizat pentru a permite accesul la informația conținută în stivă.
 

Registrele index (SI, DI) - sunt utilizate în general pentru adresarea indexată, conținând adrese relative în segmentul de date curent. Segmentul implicit utilizat în adresare poate să fie modificat prin utilizarea în instrucțiuni a unor prefixe speciale. Registrele index sunt utilizate ca registre index implicite în instrucțiunile de transfer sau prelucrare pe șiruri de octeți: SI - adresa relativă curentă în șirul sursă, DI - adresa relativă curentă în șirul destinație.


Figura 2.2 Registrele indicatoare de adresă și index

Registrele segment (CS, DS, ES, SS) - conțin adresa segmentului program (CS), adresa segmentului de date curent (DS), adresa segmentului de date suplimentar (ES) și adresa segmentului de stivă (SS).


Figura 2.3 Registrele segment

Indicatorii de condiții
Sunt utilizați pentru a memora informații referitoare la rezultatul unor operații aritmetice sau logice (AF, CF, OF, PF, SF, ZF) și pentru memorarea unor informații de control pentru microprocesor (DF, IF, TF).


Figura 2.4 Indicatorii de condiții



Setul de instrucțiuni al lui 8086

Instrucțiuni pentru transferul datelor și adreselor
MOV <destinație>, <sursă>
PUSH <sursă>
POP <destinație>
XCHG <destinație>, <sursă> - interschimbă sursa cu destinația;
XLAT - convertește conținutul registrului AL utilizând o tabelă a cărei adresă de început este în BX;
LEA <registru>, <operand> - încarcă în registru adresa relativă a operandului;
LDS <registru>, <adresă> - se încarcă în registrele DS și <registru> adresa aflată în memorie la adresa <adresă>;
LES <registru>, <adresă> - se încarcă în registrele DS și <registru> adresa aflată în memorie la adresa <adresă>;
Instrucțiuni pentru transferul valorilor indicatorilor de condiții
SAHF - încarcă indicatorii de condiții conform conținutului registrului AH;
LAHF - memorează valorile indicatorilor în registrul AH;
PUSHF  - salvează pe stivă indicatorii de condiție;
POPF - reface din stivă valorile indicatorilor de condiție.
Instrucțiuni pentru poziționarea indicatorilor de condiție
CLC - pune pe 0 valoarea indicatorului CF;
STC - pune pe 1 valoarea indicatorului CF;
CMC - comlpementează valoarea indicatorului CF;
CLD - pune pe 0 valoarea indicatorului -DF;
STD - pune pe 1 valoarea indicatorului DF;
CLI - dezactivează sistemul de întreruperi;
STI - activează sistemul de întreruperi.
Instrucțiuni aritmetice
ADD <d>, <s> - <d> Ź <d> + <s>
ADC <d>, <s> - <d> Ź <d> + <s> + CF
INC <d>  - <d> Ź <d> + 1
AAA   - corecție zecimală după adunare BCD despachetat (AL)
DAA   - corecție zecimală după adunare BCD împachetat (AL)
SUB <d>, <s> - <d> Ź <d> - <s>
SBB <d>, <s> - <d> Ź <d> - <s> - CF
DEC <d>  - <d> Ź <d> - 1
NEG <d>  - <d> Ź 0 - <d>
AAS   - corecție zecimală după scădere BCD despachetat (AL)
DAS   - corecție zecimală după scădere BCD împachetat (AL)
CBW - conversie valoare conținută în AL la cuvânt în AX (extensie de semn)
CWD - conversie valoare conținută în AX la dublu cuvânt în DX, AX (extensie de semn)
MUL <s>      - dacă <s> este de tip octet AX Ź AL * <s>
                      - dacă <s> este de tip cuvânt DX,AX Ź AX * <s>
                operanzii sunt tratați ca întregi fără semn
IMUL <s>      - dacă <s> este de tip octet AX Ź AL * <s>
                       - dacă <s> este de tip cuvânt DX,AX Ź AX * <s>
                 operanzii sunt tratați ca întregi cu semn
AAM   - corecție zecimală după o înmulțire BCD despachetat. (MUL)
DIV <s>      - dacă <s> este de tip octet AL Ź AX / <s> și AH Ź AX mod <s>
                    - dacă <s> este de tip cuvânt AX Ź DX,AX / <s> și DX Ź DX,AX mod <s>
                 operanzii sunt tratați ca întregi fără semn
IDIV <s>     - dacă <s> este de tip octet AL Ź AX / <s> și AH Ź AX mod <s>
                    - dacă <s> este de tip cuvânt AX Ź DX,AX / <s> și DX Ź DX,AX mod <s>
                 operanzii sunt tratați ca întregi cu semn
AAD - corecție zecimală înainte de o împărțire BCD despachetat. (DIV)
Instrucțiuni logice
SHL <s>, n  - deplasare logică la stânga
SAL <s>, n
SHL <s>, CL
SAL <s>, CL
SHR <s>, n  - deplasare logică la dreapta
SHR <s>, CL
SAR <s>, n  - deplasare aritmetică la dreapta
SAR <s>, CL
ROL <s>, n  - rotire stânga prin carry
ROL <s>, CL
ROR <s>, n  - rotire dreapta prin carry
ROR <s>, CL
RCL <s>, n  - rotire stânga cu carry
RCL <s>, CL
RCR <s>, n  - rotire dreapta cu carry
RCR <s>, CL
NOT <s>  - complementează față de 1 pe <s>
AND <d>, <s> - <d> Ź <d> Ù <s>
TEST <d>, <s> - se poziționează indicatorii ca la AND dar nu se modifică <d>
OR <d>, <s> - <d> Ź <d> Ú <s>
XOR <d>, <s> - <d> Ź <d> Ć <s>
CMP <d>, <s> - se poziționează indicatorii ca la SUB dar nu se modifică <d>
Instrucțiuni pe șiruri de caractere
Sursa este implicit dată de adresa DS:SI, iar destinația ES:DI. După un transfer registrele SI și DI se modifică în funcție de valoarea indicatorului DF.
MOVSB, MOVSW  - transferă un octet, cuvânt între sursă și destinație;
CMPSB, CMPSW  - poziționează indicatorii de condiție corespunzător diferenței între sursă și destinație;
SCASB, SCASW  - poziționează indicatorii de condiție conform diferenței între AL, AX și destinație;
LODSB, LODSW  - încarcă un octet, cuvânt din sursă în AL, AX;
STOSB, STOSW  - memorează conținutul AL, AX în destinație;
REP, REPZ, REPE - se repetă operația până când conținutul registrului CX devine zero sau până când indicatorul ZF devine zero.


Instrucțiuni de salt

CALL adresa  - apel de procedură;
RET   - reîntoarcere din procedură;
JZ, JE   - ZF = 1;
JL, JNGE  - mai mic (SF ? OF) - comparație valori cu semn;
JLE, JNG - mai mic sau egal (ZF = 1 sau SF ? OF) - comparație valori cu semn;
JB, JNAE, JC  - mai mic (CF = 1) - comparație valori fără semn;
JBE, JNA - mai mic sau egal (CF = 1 sau ZF = 1) - comparație valori fără semn;
JP, JPE   - PF = 1;
JO   - OF = 1;
JNZ, JNE  - ZF = 0;
JNL, JGE  - mai mare sau egal (SF = OF) - comparație valori cu semn;
JNLE, JG - mai mare (ZF = 0 și SF = OF) - comparație valori cu semn;
JNB, JAE, JNC  - mai mare (CF = 0) - comparație valori fără semn;
JNBE, JA - mai mare (CF = ZF = 0) - comparație valori fără semn;
JNP, JPO  - PF = 0;
JNO   - OF = 0;
JNS   - SF = 0;
JCXZ   - CX = 0;
Instrucțiuni de intrare/ ieșire
IN AL, port
IN AL, DX
OUT port, AL
OUT DX, AL

 


Tema de laborator

Să se implementeze următoarele rutine și macro-uri. Acestea vor fi folosite ca bază de plecare pentru celelalte laboratoare.
 
isch macro
;Testeaza daca exista caracter disponibil in buffer-ul de tastatura
; input : (none)
; output: zf  - 0 caracter disponibil
;             - 1 nu e caracter disponibil
        ...
        functia DOS 0bh
        ...
endm

getch macro
;Citeste un caracter de la tastatura
; input : (none)
; output: al - cod ASCII al caracterului citit
        ...
        functia DOS 01h
        ...
endm

putch macro c
;Afiseaza un caracter pe ecran
; input : c - cod ASCII al caracterului
; output: (none)
        ...
        functia DOS 02h
        ...
endm

delay macro n
;Introduce o intarziere de n bucle de asteptare
; input : n - numar bucle de asteptare
; output: (none)
        ...
endm

show_al_bin proc near
;Afiseaza in binar continutul lui AL
; input : al - numar 8 biti
; output: (none)
        ...
        cx Ź 8
        repeta
            daca bitul 7 din al este 0 atunci
                afiseaza ‘0’
            altfel
                afiseaza ‘1’
            al Ź al << 1
            cx Ź cx - 1
                      pana cand cx = 0
        ...
show_al_bin endp

show_ax_bin proc near
;Afiseaza in binar continutul lui AX
; input : ax - numar 16 biti
; output: (none)
        ...
        apelez show_al_bin pentru AH si apoi pentru AL
        ...
show_ax_bin endp

show_al_hex proc near
;Afiseaza in hexa continutul lui AL
; input : al - byte
; output: (none)
        ...
        * se definește un tabel care conține șirul ‘0123456789ABCDEF’
        bl Ź al
        al Ź al >> 4
        ah Ź 0
        si Ź ax
        * afișez caracterul din tabel[si]
        al Ź bl
        al Ź al & 0fh
        si Ź ax
        * afișez caracterul din tabel[si]

sau:
        dl Ź al
        dl Ź dl >> 4
        dacă dl <= 9 atunci
            dl Ź dl + ‘0’
        altfel
            dl Ź dl + ‘A’ - 10
        * afișez caracterul din registrul dl
        dl Ź al
        dl Ź dl & 0fh
        dacă dl <= 9 atunci
            dl Ź dl + ‘0’
        altfel
            dl Ź dl + ‘A’ -10
        * afișez caracterul din registrul dl
        ...
show_al_hex endp

show_ax_hex proc near
;Afiseaza in hexa continutul lui AX
; input : ax - word
; output: (none)
        ...
        apelez show_al_hex pentru AH si apoi pentru AL
        ...
show_ax_hex endp

show_al_bcd proc near
;Afiseaza in BCD continutul lui AL
; input : al - byte
; output: (none)
        ...
show_al_bcd endp

read_al_bin proc near
;Citeste un numar binar pe 8 biti
; input : (none)
; output: al - numarul citit de la tastatura
        ...
read_al_bin endp

read_al_hex proc near
;Citeste un numar hexa pe 8 biti
; input : (none)
; output: al - numarul citit de la tastatura
        ...
read_al_hex endp

calc_address proc near
;Calculeaza adresa fizica din adresa logica
; input : ax - segment
; dx - offset
; output: ax - primii 4 biti mai semnificativi din adresa fizica
; dx - ultimii 16 biti din adresa fizica
        ...
        adresa fizica = segment * 16 + offset
        ...
calc_address endp

Apelul unei proceduri:
MOV AL, 0aah ; parametru
CALL show_al_bin


Apelul unui macro:

putch ‘A’
sau
putch al


Obs.