Tutorial Limbaj de Asamblare (Assembler) Intel 8086 – Partea 7 – Lucru cu segmente

No comments -

 

In acest tutorial destinat imbajului de asamblare pentru procesoare din familia Intel 8086 sunt prezentate modalitatile de lucru cu segmente. Aceasta abordare permite realizarea de aplicatii compexe in care datele si codul nu sunt limitate doar la un singur segment.

Arhitectura de segmente

Familia de procesoare 8086 implementeaza o arhitectura de segmente prin faptul ca fiecare adresa fizica este reprezentata prin perechea adresa segment:offset.

Generatia de procesoare 8086/286 pe 16 biti permit lucru cu segmente mai mici de 64K (cea mai mare adresa posibila din cadrul unui segment – offset – este 216).

Procesoarele 90386/486 utilizeaza arhitectura pe 16 biti (segmente de maxim 64K) cand prelucreaza date in mod real, insa in mod protejat utilizeaza registre pe 32 de biti ce pot retine adrese de pana la 4 Gbytes.

Un segment fizic poate incepe numai de la o adresa divizibila cu 16, aceste locatii fiind numite (Intel) paragrafe.

Modul in care sunt definite datele si codul nu are importanta la programele mici pentru ca acestea ocupa mai putin de 64Kbytes fiind grupate in segmente individuale si sunt accesate prin intermediul offset-ului relativ la adresa de inceput a segmentului respectiv. Problema se complica in cazul programelor mari in care codul sau datele ocupa mai mult de 64K si atunci sunt segmentate. In aceasta situatie, programatorul trebuie sa se asigure ca fiecare data sau secventa de cod referita poate fi accesata prin segment:adresa.

Problema segmentarii datelor sau codului este mai putin acuta in cazul procesoarelor pe 32 de biti pentru ca segmentele sunt destul de mari (4 Gbytes) astfel incat si cele mai complexe aplicatii sa se comporte asemenea programelor mici, compacte.

 

Segmentele logice

 

Segmentele reprezinta seturi de date sau instructiuni ce sunt disponibile prin intermediul offset-ului relativ de adresa de inceput a segmentului.

Adresele fizice ale segmentelor sunt cunoscute prin intermediul registrelor de segment:

Registru

Descriere

DS

- segmentul de date

CS

- segmentul de cod

SS

- segmentul de stiva

ES

- segmentul extins

, insa trebuie avut in vedere faptul ca, in cazul in care exista mai multe segmente definite de programator, numai unul dintre ele, la un moment dat, poate fi asociat cu segmentele logice de date, de cod, de stiva sau extins.
 

La incarcarea in memorie a programului, sistemul initializeaza registrul de segment CS cu prima adresa se segment disponibila instructiune, iar registrul IP cu adresa relativa din cadrul segmentului, a primei instructiuni ce trebuie executata. Segmentele logice se incarca in memorie in ordinea cod, date, stiva.

 

Definirea segmentelor

 

Segmentele logice contin cele tei componente ale unui program: cod, date si stiva. Modul in care pot fi specificate segmentele sunt:

  • definire simplificata;
  • definire completa a segmentelor.

Definirea simplificata ascunde multe detalii ale definirii segmentelor si utilizeaza aceleasi conventii implementate de Microsoft in limbajele de nivel inalt.

Structura unui program ale carui segmente sunt descrise utilizand definirea simplificata este: (descrisa si in prima parte a tutorialului)

.MODEL [tip_model] [conventia_de_limbaj] \	;declaratie obligatorie inainte de a 
[tip_stiva]			                ;utiliza directivele simplificate de 
                                                ;definire a unui segment
[.tip_procesor]				;definire tip procesor, implicit 8086 
.STACK [expresie]			;definire segment de stiva, implicit 1KB (1024 
                                                ;de octeti)
.DATA					;definire segment de date
 
.CODE					;definire segment de cod
start:					;eticheta ce indica adresa de start a codului
 
END start				;indica sfarsit program - obligatoriu

in care:

Optiune

Descriere

Valori posibile

tip_model

defineste modelul de memorie; determina dimensiunea codului si datelor (segmentelor) precum si a adreselor;

TINY, SMALL, COMPACT, MEDIUM, LARGE, HUGE, FLAT

conventia_de_limbaj

faciliteaza compatibilitatea cu limbajele de nivel inalt prin determinarea codarii interne a numelor simbolurilor externe si publice;

C, BASIC,FORTRAN, PASCAL, SYSCALL, STDCALL

tip_stiva

modul in care este definita stiva ca distanta fata de segmentul de date; NEARSTACK plaseaza stiva in acelasi segment fizic cu cel al datelor; pentru FARSTACK stiva are propriul ei segment fizic;

NEARSTACK (implicit), FARSTACK

tip_procesor

odata selectat tipul procesorului se utilizeaza doar setul de instructiuni asociat;

.186, .286, .386, .486

expresie

dimensiunea in octeti a stivei

 
     

Pot fi utilizate o serie de simboluri predefinite pentru a descrie segmentele:

Simbol

Descriere

@Model

- intoarce modelul de memorie ca valoare intreaga cuprinsa intre 1 si 7;

@Data

- segmentul de date

@DataSize

- dimensiunea modelului de memorie

@CurSeg

- segmentul curent

@CodeSize

- tipul pointerilor: far sau near

   

Definirea completa a segmentelor utilizeaza sintaxa:

nume_segment SEGMENT [tip_aliniere] [READONLY] [tip_combinare] [tip_adresare] [clasa_segmente]

………

nume_segment ENDS

 

in care:

Element

Descriere

nume_segment

- defineste numele segmentului; in cadrul unui modul toate segmentele definite cu acelasi nume sunt considerate ca reprezentand acelasi segment; de asemenea sunt combinate si segmentele cu acelasi nume, dar care se gasesc in module diferite.

tip_aliniere

- descrie adresa la care incepe un segment nou

READONLY

- indica programului ca segmentul este de tip read-only si se genereaza eroare la incercarile de modifica valori; element optional;

tip_combinare

- indica modul in care sunt combinate segmentele cu acelasi nume dar din module separate la crearea executabilului;

tip_adresare

- descrie modul de lucru al procesoarelor (de la 386 in sus), fie pe 16 biti, fie pe 32 de biti;

clasa_segmente

- nume dat intre ” ce indica o clasa de segmente; segmentele din aceeasi clasa sunt aranjate secvential; utilizat pentru a indica ordinea de aranjare a segmentelor.

Atentie ! Nu se poate specifica mai mult de o valoarea pentru fiecare element.

Un segment se poate inchide cu ENDS si apoi redeschide ulterior doar cu directiva nume_segment SEGMENT, fara a modifica optiunile din prima definire.

 

[tip_aliniere]

  • element optional;
  • defineste multimea din care sa fie selectata adresa de la care este alocat segmentul;
  • descrie faptul ca adresa fizica de inceput a segmentului este divizibila cu 1,2,4,16 sau 265;
  • link-erul utilizeaza aceasta informatie pentru a determina adresa relativa de start a fiecarui segment; sistemul determina adresa de start actuala cand programul este incarcat;
  • valorile pe care le poate lua:

Tip aliniere

Adresa de start

BYTE

- urmatoarea adresa de tip byte disponibila;

WORD

- urmatoarea adresa de tip word disponibila;

DWORD

- urmatoarea adresa de tip double disponibila;

PARA

- urmatoarea adresa de tip paragraf (16 octeti) disponibila; valoare default

PAGE

- urmatoarea adresa de tip pagina (256 octeti) disponibila;

   

[tip_combinare]

  • element optional;
  • indica modul in care sunt combinate segmente cu acelasi nume, dar care apar in module diferite; informatie utilizata de link-er, nu de asamblor, pentru ca este necesara la crearea executabilului si nu la asamblarea fiecarui modul in parte;
  • valorile pe care le poate lua:

Tip combinare

Descriere

PRIVATE

- nu combina segmentul respectiv cu alte segmente (din alte module) ce au acelasi nume; optiune default;

PUBLIC

- combina segmentele cu acelasi nume din module separate intr-o zona de memorie continua;

COMMON

- combina segmentele cu acelasi nume din module separate suprapunandu-le pe aceeasi zona de memorie de marime egala cu dimensiunea celui mai mare segment;

AT expresie

- segmentul este plasat la adresa indicata de expresie; nu se permite declararea de cod sau initializare de date; reprezinta o masca care se poate suprapune datelor si codului existent in memorie; asamblorul nu genereaza date sau cod pentru acest segment, insa permite adresarea in cadrul sau;

MEMORY

- segmentul curent este asezat in memorie in spatiul ramas disponibil dupa alocarea celorlalte segmente (catre sfarsitul memoriei); utilizat si ca sinonim pentru PUBLIC;

STACK

- concateneaza toate segmentele cu acelasi nume si pune in SS adresa de inceput a segmentului rezultat, iar SP pe sfarsitul sau; segmentul rezultat este asociat stivei;

[tip_adresare]

  • element optional;
  • reprezinta atributul modului de utilizare pentru procesoare, incepand de la .386;
  • valorile pe care le poate lua:

Tip adresare

Descriere

USE16

- indica faptul ca elementele din segment sunt adresate utilizand un offset pe 16 de biti; daca se specifica .386 sau .486 dupa de directiva .MODEL atunci default este modul USE16;

USE32

- indica faptul ca elementele din segment sunt adresate utilizand un offset pe 32 de biti; daca se specifica .386 sau .486 inainte de directiva .MODEL atunci default este modul USE32;

[clasa_segmente]

  • element optional;
  • indica modul de aranjare in memorie a segmentelor din aceeasi clasa;
  • doua segmente cu acelasi nume, dar cu clase diferite nu sunt combinate;
  • segmentele din aceeasi clasa sunt aranjate secvential in memorie in ordinea in care sunt intalnite.

In mod normal, asamblorul aranjeaza segmentele in fisierul .obj in ordinea in care apar in codul sursa. In schimb, link-editorul proceseaza fisierele .obj in ordinea din linia de comanda. In cadrul fiecarui modul, acesta aloca memorie pentru segmente in ordinea aparitiei, apartenentei la un grup, clasa si cerinte date de modul de aranjare .DOSSEG (conventia de ordonare a segmentelor MS-DOS).

Modul de aranjare a segmentelor este important atunci cand e doreste ca anumite segmente sa apara la inceputul sau sfarsitul unui program sau cand se fac presupuneri in legatura cu ce segmente se gasesc langa altele.

Pentru a controla ordinea in care segmentele apar in memorie se utilizeaza directivele:

.SEQ

- in ordinea in care sunt declarate; modalitate implicita

.ALPHA

- ordonare alfabetica a segmentelor dintr-un modul;

.DOSSEG

- aranjeaza segmentele in ordinea standard a limbajelor Microsoft; nu se utilizeaza directiva in module apelate in cadrul altor module; ordinea segmentelor este cod, date, stiva.

 

Initializarea segmentelor de registru

In cadrul programelor cu structura simplificata (.model) initializarea registrelor de segment (DS, SS, ES, CS) si asocierea lor cu segmentele de date, cod si stiva era realizata de asamblor si lnk-editor. In cadrul programelor a caror structura este definita de programator, initializarea acestora se face de catre acesta. Pasii ce trebuie urmati sunt:

Atentie !
Asocierea logica dintre registrul de segment si segmentul declarat se face prin intermediul directivei ASSUME. Aceasta informatie este necesara asamblorului si link-editorului la transformarea programului in cod masina.

Multe dintre instructiunile assembler presupun existenta unui segment implicit. Accesul la o zona de memorie din segmentul de date fie prin nume variabila, [registru index] sau [registru de baza][registru index] (toate reprezinta adrese offset) se realizeaza fata de adresa fizica continuta de registrul DS. Fiecare instructiune de citire/scriere cu stiva (POP/PUSH) se realizeaza implicit fata de SS. Instructiunile de salt conditionat/neconditionat reprezinta salturi la adrese offset in cadrul segmentului de cod, a carui adresa se gaseste in CS.

De aceea in cazul programelor cu mai multe segmente este necesar sa se indice asocierea logica dintre segment si registru de segment pentru ca programul (mai precis asamblorul si link-editorul care transforma codul in cod masina) sa stie unde (in ce segmente) se gasesc adresele respective (date sau instructiuni).

Asocierea se realizeaza cu instructiunea

ASSUME registru_segment:nume_segment[,registru_segment:nume_segment]
ASSUME [registru_segment:] NOTHING [,registru_segment:NOTHING]

Un registru_segment reprezinta unul dintre registrele de segment: CS, DS, ES sau SS; nume_segment reprezinta numele unui segment sau grup de segmente.

Atentie !
Odata ce s-a modificat unul dintre registrele DS, ES sau SS trebuie precizat acest lucru procesorului prin incarcarea registrului de segment utilizat implicit cu adresa noului segment. Daca segmentul utilizat reprezinta o zona de date initializarea registrului DS se face prin secventa:

mov AX, nume_segment
mov DS, AX

Daca segmentul reprezinta o zona de stiva initializarea registrului SS se realizeaza prin:

mov AX, nume_segment
mov SS, AX
mov SP, offset baza_stiva	;pentru ca adresele in stiva scad trebuie ca SP sa fie ;initializat cu offset-ul ultimei zone de memorie din stiva

Modul simplificat de definire a unui segment ce va fi utilizat pe post de stiva este:

Stiva SEGMENT
	dw 50 dup (?)	;rezerv o zona de 50 * 2 octeti
	baza_stiva label word
Stiva ENDS

Daca segmentul utilizat reprezinta o zona de date extinsa initializarea registrului ES se face prin secventa:

mov AX, nume_segment
mov ES, AX

Se observa ca registrul CS nu trebuie incarcat. Acest lucru este realizat implicit (daca nu s-ar fi realizat acest lucru nu se putea executa nici o instructiune din program ,iar initializarile reprezinta de fapt secvente de instructiuni) prin indicarea punctului de start al programului (NU UITA de eticheta start: si de instructiunea de final end start) si prin asocierea logica ASSUME CS:nume_segment.

Instructiunea ASSUME registru_segment:NOTHING sterge asocierea logica anterioara. Instructiunea ASSUME NOTHING sterge asocierile logice pentru toate registrele de segment.

 

Definirea grupurilor de segmente

 

Un grup reprezinta o colectie de segmente ce nu trebuie sa depaseasca (cumulat !!!) mai mult de 64 KBytes pe arhitectura de 16 biti si nu mai mult de 4GBytes pe 32 de biti. Un grup permite asocierea dintre mai multe segmente definite logic si elimina repetarea instructiunilor de incarcare a registrelor de segment la schimbarea registrelor utilizate. De exemplu programul contine mai multe segmente de date iar accesul la ele implica schimbarea adresei din registrul DS. Gruparea acestor segmente intr-un grup implica realizarea unui singur segment obtinut prin concatenarea segmentelor din grup si incarcarea o singura data a registrului DS cu adresa grupului. Toate datele/codul este astfel accesibil relativ la adresa de inceput a grupului. Sintaxa de definire a grupului este:

nume_grup GROUP nume_segment [, nume_segment]

Atentie ! Se are in vedere:

  • un segment nu poate fi definite in mai multe grupuri
  • dimensiunea grupului nu trebuie sa depaseasca 65 KBytes pe 16 biti si 4 GBytes pe 32 de biti;
  • nu indica modul de incarcare in memorie a segmentelor din grup si de aceea trebuie acordata atentie la modul de definire a segmentelor astfel incat segmente ce nu apartin grupului sa nu fie alocate intre segmentele din grup, iar in final acesta sa depaseasca dimensiunea maxima admisa.

 

De exemplu secventa de program:

Date1 SEGMENT
	a dw 1234h
	b dw 5678h
Date1 ENDS
 
Date2 SEGMENT
	vector dw 5 dup (9)
Date2 ENDS
 
Temporare SEGMENT
	temp dw 5 dup (3)
Temporare ENDS
 
Date3 SEGMENT
	c dw 1111h
	d dw 2222h
Date3 ENDS
 
Stiva SEGMENT
	dw 100 dup(?)
	varf label word
Stiva ENDS
 
Date GROUP Date1, Date2, Date3
 
Principal SEGMENT
	ASSUME CS:Principal,SS:Stiva,DS:Date
start:
	mov AX,Date	;incarc in DS adresa grupului de segmente
	mov DS,AX
 
	mov AX,Stiva	;incarc in SS adresa segmentului de stiva
	mov SS,AX
	mov AX, offset varf	;incarc SP
	mov SP,AX
 
	mov AX, b		;pun in AX valoarea din b
	mov AX, vector	;pun in AX valoarea din vector[0]
	mov AX, c		;pun in AX valoarea din c
	mov AX, d		;pun in AX valoarea din d
 
;se observa ca datele sunt accesibile fara a incarca de fiecare data DS cu adresa 
;segmentului parinte
	mov AX,4c00h
	int 21h
Principal ENDS
 
end start

genereaza dispunerea in memorie a datelor astfel:

1
2
3
4
5
6
7
DS:0000	34 12 78 56 00 00 00 00	;date segment Date1
DS:0008	00 00 00 00 00 00 00 00	;zona de date
DS:0010	09 00 09 00 09 00 09 00	;date segment Date2
DS:0018	09 00 00 00 00 00 00 00	;date segment Date2
DS:0020	03 00 03 00 03 00 03 00	;date segment Temporare
DS:0028	03 00 00 00 00 00 00 00	;date segment Temporare
DS:0030	11 11 22 22 00 00 00 00	;date segment Date3

Atentie !De ce fiecare segment pare ca ocupa 16 octeti (fiecare segment ocupa atata spatiu cate date sunt definite in el, iar restul de octeti sunt nerezervati) ? Pentru ca implicit la definire tipul alinierii este PARA segmentele sunt alocate in memorie numai de la adrese divizibile cu 16. Restul de octeti (pana la al 16-lea) sunt nerezervati, dar pot fi accesati relativ fata de adresa de inceput a segmentului.

Atentie ! In exemplul anterior instructiunea mov AX,offset d va pune in AX valoarea 0002h si nu 0030h deoarece intoarce offset-ul fata de adresa segmentului parinte si nu fata de adresa de start a grupului.

In Debugger (la dezasamblarea instructiunilor) se observa ca variabilele b, vector, c si d sunt referite prin offset-uri relative fata de adresa de inceput a grupului: 0002h, 0010h, 0030h, 0032h evidentiindu-se faptul ca intre segmentele grupului s-a intercalat segmentul Temporare (ordinea de alocare in memorie este implicit .SEQ). Pentru a evita situatia:

  • se declara segmentul Temporare dupa declararea celor 3 segmente Date1, Date2 si Date3;
  • se utilizeaza o clasa de segmente ‘clasa_seg’ in care sunt puse segmentele Date1, Date2 si Date3.

De exemplu:

Date1 SEGMENT 'clasa_date'
	a dw 1234h
	b dw 5678h
Date1 ENDS
 
Date2 SEGMENT 'clasa_date'
	vector dw 5 dup (9)
Date2 ENDS
 
Temporare SEGMENT BYTE	;alocare la o adresa divizibila cu 1
	temp dw 5 dup (3)
Temporare ENDS
 
Date3 SEGMENT 'clasa_date'
	c dw 1111h
	d dw 2222h
Date3 ENDS
 
Stiva SEGMENT
	dw 100 dup(?)
	varf label word
Stiva ENDS
 
Date GROUP Date1, Date2, Date3
.................

In aceasta situatie segmentele sunt aranjate in memorie astfel:

1
2
3
4
5
6
DS:0000	34 12 78 56 00 00 00 00	;date segment Date1
DS:0008	00 00 00 00 00 00 00 00	;zona de date
DS:0010	09 00 09 00 09 00 09 00	;date segment Date2
DS:0018	09 00 00 00 00 00 00 00	;date segment Date2
DS:0020	11 11 22 22 03 00 03 00	;date segment Date3 si Temporare 
DS:0028	03 00 03 00 03 00 00 00	;date segment Temporare

Se observa ca indicand ca segmentul Temporare sa fie alocat la o adresa de tip BYTE el urmeaza dupa segmentul Date3 eliminandu-se octetii nefolositi.

 

 

,


  1. No comments yet.
(will not be published)

  1. No trackbacks yet.