# 07 - Introducere în Assembly

## Registrii microprocesorului

Registrii într-un microprocesor sunt unități mici de stocare extrem de rapide, localizate în interiorul CPU (Unitatea Centrală de Procesare). Acești registri sunt utilizați pentru a accesa și stoca rapid date sau instrucțiuni care sunt imediat sau frecvent necesare în timpul execuției programelor. Ei joacă un rol crucial în funcționarea procesorului. Iată câteva caracteristici și funcții ale acestor registri:

1. **Acces Rapid**: Registrii sunt cele mai rapide locații de memorie disponibile într-un computer. Accesarea datelor dintr-un registru este mult mai rapidă decât accesarea datelor din RAM sau alte dispozitive de stocare.
2. **Număr Limitat și Dimensiune**: Spre deosebire de RAM, numărul de registri este foarte limitat, iar dimensiunea lor este, de obicei, mică (de exemplu, 32 de biți, 64 de biți).
3. **Tipuri de Registri**:
   * **Registri Generali**: Folosiți pentru operații aritmetice, logice și alte operații generale.
   * **Registri de Date**: Păstrează valori numerice și sunt utilizați în operații aritmetice, logice și alte operații cu date.
   * **Registri de Adrese**: Stochează adrese de memorie și sunt folosiți pentru a accesa date și instrucțiuni din memorie.
   * **Registri Pointer de Stivă și Registri Pointer de Bază**: Folosiți în gestionarea apelurilor de funcții și operațiunilor de stivă.
   * **Registri de Segment**: În unele arhitecturi (cum ar fi x86 în mod real), ele definesc locația segmentelor de memorie.
   * **Contor de Instrucțiuni sau Contor de Program**: Păstrează adresa următoarei instrucțiuni care urmează să fie executată.
   * **Registru de Stare (Flags Register)**: Conține marcaje (flags) care indică rezultatul operațiunilor (de exemplu, zero flag, carry flag, etc.) și controlează anumite operațiuni ale CPU.

### Registrii în Intel 80286 <a href="#registrii-in-intel-80286" id="registrii-in-intel-80286"></a>

Procesorul Intel 80286, un CPU de 16 biți folosit în computerele compatibile IBM PC în anii '80, dispune de mai multe tipuri de registri:

1. **Registri Generali**:
   * AX, BX, CX, DX: Registri principali de 16 biți folosiți pentru diverse operații.
   * SI, DI: Index Sursă (SI) și Index Destinație (DI) pentru operații cu șiruri și array-uri de memorie.
   * BP: Pointer de Bază (BP) pentru operațiuni pe stivă.
   * SP: Pointer de Stivă (SP) pentru a indica poziția curentă în stivă.
2. **Registri de Segment**:
   * CS: Segment de Cod.
   * DS: Segment de Date.
   * SS: Segment de Stivă.
   * ES: Segment Suplimentar.
3. **Contor de Instrucțiuni**:
   * IP: Contor de Instrucțiuni (sau Contor de Program).
4. **Registru de Stare**:
   * FLAGS: Registru de 16 biți care conține steaguri de rezultat ale operațiunilor și controlează operațiuni specifice CPU.

În total, 80286 are 14 registri principali: 8 registri generali, 4 registri de segment, contorul de instrucțiuni și registru de stare. Este important de notat că, spre deosebire de succesorii săi (80386 și ulterior), 80286 nu are registri extinși de 32 de biți (cum ar fi EAX, EBX, etc.), fiind un procesor de 16 biți.

### Registrul AX <a href="#registrul-ax" id="registrul-ax"></a>

Registrul `AX`, cunoscut ca „Accumulator”, este unul dintre registrii principali în arhitectura procesorului x86 și este adesea implicat într-o varietate largă de operații tipice în limbajul de asamblare. Iată câteva dintre cele mai comune utilizări ale registrului `AX`:

1. **Operații Aritmetice**: `AX` este frecvent utilizat în operațiile aritmetice, cum ar fi adunarea (`ADD`), scăderea (`SUB`), înmulțirea (`MUL`) și împărțirea (`DIV`). În cazul înmulțirii și împărțirii, `AX` este implicit utilizat pentru a stoca unul dintre cei doi operanzi si apoi rezultatul operatiei.
2. **Instrucțiuni de Transfer de Date**: Este folosit în mod obișnuit pentru a stoca valori care vor fi transferate în alte registre sau în memorie și invers. De exemplu, instrucțiunea `MOV AX, data` încarcă valoarea `data` în `AX`.
3. **Interruperi și Apeluri de Sistem**: În apelurile de sistem DOS (de exemplu, `int 21h`), `AX` este folosit pentru a stoca codul funcției apelului de sistem și, uneori, pentru a returna valori.
4. **Operații Logice și Comparare**: `AX` poate fi folosit pentru a efectua operații logice (cum ar fi `AND`, `OR`, `XOR`) și pentru comparații (de exemplu, `CMP AX, BX`).

{% hint style="info" %}
În arhitectura x86, instrucțiunile de asamblare nu există în formă textuală în timpul execuției, ci sunt codificate sub formă de secvențe de octeți (cod mașină) în memorie. Atunci când scrii în cod sursă o instrucțiune precum `mov ax, 34`, assembler-ul va traduce această instrucțiune în cod binar corespunzător, pe care linker-ul apoi îl va așeza în secțiunea de cod executabil a programului.

**Exemplu concret:** Instrucțiunea `mov ax, 34` în asamblare (unde `34` este un număr zecimal, echivalent cu `0x22` în hexazecimal) se traduce, în mod real (16 biți), aproximativ astfel:

1. **Identificarea opcodului:** Pentru a încărca un registru de 16 biți (ax, bx, cx, dx etc.) cu o valoare imediată se folosesc instrucțiuni din familia `MOV r16, imm16`. Cea pentru `ax` are un opcode specific: `B8` urmat de valoarea imediată pe 16 biți.
2. **Valoarea imediată:** Valoarea 34 în decimal este 0x22 în hexazecimal. Reprezentarea pe 16 biți este `0x0022` (octeții în memorie vor fi în ordine little-endian: mai întâi byte-ul cel mai mic, apoi cel mare).
3. **Codul rezultat:**
   * Opcode: `B8` (indică `mov ax, imm16`)
   * Imediata: `22 00` (0x22 este byte-ul low, 0x00 byte-ul high)

Prin urmare, instrucțiunea `mov ax, 34` se stochează în memorie ca următoarea secvență de octeți:

`B8 22 00`

Astfel, procesorul, citind din memoria executabilă, va interpreta acești 3 octeți ca fiind instrucțiunea `MOV AX, 0x0022`.

**Pe scurt:**

* Instrucțiunile sunt stocate în memorie sub formă de cod numeric (cod mașină).
* Assembler-ul traduce codul sursă (mnemonice ca `mov`, `add`, `jmp` etc.) în opcode-uri și operanzi sub formă de octeți.
* Procesorul parcurge memoria, citește secvențele de octeți și decodifică instrucțiunea pe baza opcodului și a celorlalți parametri codificați în acei octeți.
  {% endhint %}

{% hint style="warning" %}
**Endianness** se referă la modul în care un sistem stochează în memorie octeții ce compun cuvintele mai mari de un octet (de exemplu, un cuvânt de 16 biți sau un double word de 32 biți). În big endian, octetul cel mai semnificativ este stocat la adresa cea mai mică din memorie, urmat de ceilalți în ordine descrescătoare de semnificație, în timp ce în little endian ordinea este inversă: octetul cel mai puțin semnificativ se află la cea mai mică adresă, urmat de cel mai semnificativ.

Arhitectura x86, inclusiv procesorul Intel 80286, utilizează formatul little endian pentru stocarea datelor în memorie.
{% endhint %}

### Registrul BX <a href="#registrul-bx" id="registrul-bx"></a>

Registrul `BX`, cunoscut ca „Base Register”, este un alt registru important în arhitectura procesorului x86. Deși este versatil și poate fi utilizat în diverse moduri, există câteva scopuri și operații tipice în care `BX` este frecvent implicat:

1. **Accesare Directă a Memoriei**: Datorită modului său de adresare, `BX` este deseori folosit în scenarii unde este necesar acces direct la o anumită locație de memorie. Ex: `MOV AL, [BX]`
2. **Instrucțiuni de Transfer de Date**: Similar cu alte registri, `BX` este utilizat pentru a stoca date care vor fi transferate din sau în memorie. De exemplu, `MOV BX, data` încarcă valoarea `data` în `BX`.
3. **Operații Aritmetice și Logice**: Deși nu este la fel de central ca `AX` în operații aritmetice, `BX` poate fi folosit în adunări, scăderi, operații logice (cum ar fi `AND`, `OR`, `XOR`) și comparații.
4. **Adresare Bazată pe Index**: `BX` este adesea folosit în moduri de adresare complexe, cum ar fi adresarea bazată pe index. În aceste cazuri, `BX` poate fi folosit pentru a reține adresa de bază a unui array sau a unei structuri de date în memoria programului.

### Registrul CX <a href="#registrul-cx" id="registrul-cx"></a>

Registrul `CX`, cunoscut drept „Count Register”, în arhitectura procesorului x86, este adesea utilizat în operatii specifice care necesită numărătoare sau repetiții. Iată câteva dintre utilizările tipice ale registrului `CX`:

1. **Bucle și Repetiții**: `CX` este folosit frecvent ca un contor în bucle. De exemplu, într-o buclă `LOOP`, `CX` este decrementat automat la fiecare iterație a buclei, iar bucla continuă până când `CX` ajunge la zero.

   ```asm
   MOV CX, 10   ; Inițializează CX la 10
   bucla:       ; Eticheta buclei
       
       ; ... codul buclei ...

   LOOP bucla   ; Decrementază CX și repetă bucla dacă CX nu este 0
   ```
2. **Instrucțiuni de Shift și Rotate**: În unele cazuri, `CX` este folosit pentru a specifica numărul de biti pentru instrucțiuni de shift (de exemplu, `SHL` sau `SHR`) și rotate (de exemplu, `ROL` sau `ROR`).
3. **Operații Aritmetice și Logice**: Deși nu este la fel de comun ca `AX` sau `BX`, `CX` poate fi folosit și în operații aritmetice de bază sau operații logice.
4. **Instrucțiuni de Transfer de Date**: Similar cu alte registri, `CX` poate fi folosit pentru a stoca date temporar în timpul execuției programului.

### Registrul DX <a href="#registrul-dx" id="registrul-dx"></a>

Registrul `DX`, cunoscut ca "Data Register" în arhitectura procesorului x86, este utilizat într-o varietate de operații, având roluri specifice în multe scenarii de programare la nivel de asamblare. Iată câteva dintre utilizările tipice ale registrului `DX`:

1. **Împărțire și Înmulțire Extinsă**: În operațiile de înmulțire (`MUL`) și împărțire (`DIV`) care necesită sau produc rezultate pe 32 de biți într-un procesor de 16 biți, `DX` este folosit împreună cu `AX`. Pentru `MUL`, `DX:AX` formează rezultatul extins, iar pentru `DIV`, `DX` stochează restul, iar `AX` rezultatul împărțirii.

   ```asm
   MOV AX, 1234h  ; Valoarea inițială pentru AX
   MOV DX, 0      ; Inițializează DX la 0 pentru extinderea pe 32 de biți
   MUL BX         ; Înmulțește AX cu BX, rezultatul pe 32 de biți în DX:AX
   ```
2. **Operații de Intrare/Ieșire**: `DX` este frecvent utilizat pentru a specifica un port de intrare/ieșire în operațiile de I/O. De exemplu, `IN` și `OUT` folosesc `DX` pentru a indica numărul portului.

   ```asm
   MOV DX, portNumber ; Setează numărul portului de I/O
   IN AL, DX         ; Citește un byte din portul de I/O în AL
   ```
3. **Adresare Indirectă**: În anumite moduri de adresare, `DX` poate fi folosit pentru a reține o adresă de memorie indirectă, similar cu registrii `BX` și `SI`.
4. **Transfer de Date și Manipulări**: Asemenea altor registri generali, `DX` poate fi folosit pentru transferul de date temporare și pentru diverse manipulări în cadrul programului.

## Functionarea si Anatomia unui program de calculator

### Segmentarea memoriei <a href="#segmentarea-memoriei" id="segmentarea-memoriei"></a>

Segmentarea memoriei în procesorul Intel 80286 este o caracteristică esențială a arhitecturii sale, care îi permite să acceseze și să gestioneze memoria într-un mod eficient și flexibil. Acest procesor, care este parte a familiei x86, introduce un model de memorie segmentată care se deosebește de managementul liniar al memoriei folosit în sistemele mai simple.

**Conceptul de Segmentare**

* **Segmente de Memorie**: În 80286, memoria este împărțită în segmente. Fiecare segment este o porțiune de memorie cu o adresă de start și o lungime specifică.
* **Registri de Segment**: Procesorul folosește registri de segment pentru a accesa diferite părți ale memoriei. Registrii standard de segment sunt: `CS` (Code Segment), `DS` (Data Segment), `SS` (Stack Segment) și `ES` (Extra Segment).

**Adresare**

* **Adresă Fizică**: Adresa fizică a memoriei este calculată combinând un registru de segment cu un offset. De exemplu, adresa fizică este obținută prin înmulțirea valorii din registru de segment cu 16 (decalarea la stânga cu 4 biți) și adăugând offset-ul.
* **Exemplu de Calcul**: Dacă `DS` este `2000h` și offset-ul este `1234h`, adresa fizică va fi `(2000h * 10h) + 1234h = 201234h`.

**Moduri de Operare**

* **Real Mode**: În modul real (real mode), 80286 se comportă similar cu 8086, având o limită de adresare de 1MB. Segmentarea este folosită pentru a depăși această limită într-un anumit sens, permițând programelor să acceseze diferite blocuri de 64KB în cadrul limitei de 1MB.
* **Protected Mode**: 80286 introduce modul protejat (protected mode), care permite controlul mai bun al memoriei și protecția. În acest mod, segmentele pot fi de diferite mărimi și sunt gestionate printr-un tabel de descriptori de segment, oferind acces la un spațiu de adresare mai mare (până la 16MB).

**Utilizări**

* **Gestionarea Memoriei**: Segmentarea permite programelor să gestioneze memoria în moduri mai eficiente, separând codul, datele și stiva.
* **Protecție și Securitate**: În modul protejat, segmentarea oferă mecanisme de protecție care previn accesul neautorizat la segmente de memorie.

{% hint style="info" %}
Segmentarea în procesorul 80286 reprezintă un pas important în evoluția arhitecturii x86, oferind o flexibilitate și control sporite în gestionarea memoriei. Această abordare a fost fundamentală în dezvoltarea ulterioară a arhitecturilor de procesor și a sistemelor de operare moderne. Cu toate acestea, complexitatea adăugată de segmentare a dus în cele din urmă la preferarea altor modele de gestionare a memoriei, cum ar fi paginarea, în arhitecturile mai noi.
{% endhint %}

### Executia unui program de calculator <a href="#executia-unui-program-de-calculator" id="executia-unui-program-de-calculator"></a>

Inainte de executia primei instructiuni din cadrul unui program de calculator, sistemul de operare, realizeaza urmatorele operatii:

* incarca in memoria sistemului lista de instructiuni si argumentele aferente
* rezerva in memoria sistemului spatiu pentru variabile, si le initializeaza daca este cazul
* rezerva in memoria sistemului spatiu pentru stiva

In cadrul unui program in Assembly, informatiile pentru realizarea acestor operatii se gasesc in segmentele de cod, de date, respectiv de stiva, care sunt definite cu directivele:

* `.code`
* `.data`
* `.stack`

Directiva `.model` descrie modul in care cele 3 categorii de informatii vor fi stocate la nivelul memoriei: pe acelasi segment, pe segmente distincte, sau pentru programe cu necesar mai mare de resurse de calcul, pe segmente multiple pentru cod, variabile sau stiva.

### Întreruperile Software <a href="#intreruperile-software" id="intreruperile-software"></a>

* **Definiție**: O întrerupere software este un mecanism care permite software-ului să întrerupă executia instructiunilor in ordinea din segmentul de cod și să apeleze un serviciu al sistemului de operare. Acest lucru se face de obicei printr-o instrucțiune specială în limbajul de asamblare, cum ar fi `int` în asamblarea x86.
* **Scop**: Întreruperile software oferă o modalitate pentru programe de a solicita servicii de la sistemul de operare, cum ar fi operații cu fișiere, gestionarea memoriei, afișarea in consolă sau citirea de la tastatura.
* **Utilizare**: Programul setează anumite registre, și uneori stiva, cu parametrii necesari și apoi execută instrucțiunea de întrerupere. CPU transferă apoi controlul la handler-ul de întrerupere, care citește acești parametri și efectuează serviciul solicitat.

### DOS `int 21h` <a href="#dos-int-21h" id="dos-int-21h"></a>

* În sistemul de operare MS-DOS, `int 21h` este o întrerupere multiplă folosită pentru a apela serviciile DOS și oferă o gamă largă de funcții, de la operațiuni cu fișiere la introducerea / afisarea de caractere și altele. Vezi mai multe in sectiunea: [Instrumente si linkuri utile](https://mihai-gheorghe.gitbook.io/bti-introducere-in-assembly/instrumente-si-linkuri-utile#lista-apelurilor-de-sistem-ms-dos-intreruperi-int-21h)
* **Specificarea Funcției**: Functia de sistem care urmează să fie invocata este specificata în registrul `AH`, iar parametrii suplimentari pot fi plasați în alte registre sau locații de memorie, în funcție de funcția specifică apelată.
* **Exemplu**: De exemplu, pentru a scrie un caracter la consolă, se încarcă `AH` cu numărul funcției (de exemplu, `02h` pentru scrierea unui caracter), se încarcă codul caracterului în registrul `DL` și apoi execută `int 21h`.

### Program care afiseaza un sir de caractere stocat in memorie: <a href="#program-care-afiseaza-un-sir-de-caractere-stocat-in-memorie" id="program-care-afiseaza-un-sir-de-caractere-stocat-in-memorie"></a>

```asm
.model small
.stack 200h
.data
    mesaj db "Salut$"
.code
    programulPrincipal:
        mov ax, @data
        mov ds, ax

        mov dx, offset mesaj
        mov ah, 09h
        int 21h

        mov ah, 4ch
        int 21h
    end programulPrincipal
```

si varianta cu comentarii / explicatii:

```asm
.model small
;alocarea segmentelor de cod, date si stiva in acelasi segment de memorie

.stack 200h
;alocarea unui segment de stiva de 512 bytes

.data
    mesaj db "Salut$"
    ;definirea unei variabile, "mesaj", ca succesiune de octeti
    ;db: define bytes
    ;practic variabila "mesaj" va indica pozitia din memorie
    ;unde se gaseste codul ASCII al caracterului "S"
    ;urmatoarea pozitie de memorie contine codul caracterului "a"
    
.code
    programulPrincipal:
        mov ax, @data
        mov ds, ax
        ;@data face referire la adresa segmentului unde au fost
        ;declarate si initializate variabilele
        ;nu se poate scrie din memorie direct in registrul DS
        ;astfel ca acest lucru se realizeaza in 2 pasi, prin AX

        mov dx, offset mesaj
        ;in DX se retine adresa de unde "incepe" variabila mesaj
        mov ah, 09h
        ;09h este codul intreruperii software DOS care
        ;afiseaza un sir de caractere stocat in memorie
        ;incepand de la adresa DS:DX
        ;pana la intalnirea codului ASCII al "$"
        int 21h
        ;instructiunea care invoca intreruperea corespunzatorare
        ;codului din AH

        mov ah, 4ch
        ;codul intreruperii care reda controlul sistemului de operare
        int 21h
        ;invocarea efectiva a intreruperii
    end programulPrincipal
```

### Executia unui program in TASM <a href="#executia-unui-program-in-tasm" id="executia-unui-program-in-tasm"></a>

Deoarece discutam despre o arhitectura a unor sisteme disponibile in prima parte a anilor 1980, pentru a putea executa executa un program in asamblare pentru o astfel de configuratie, este necesar sa utilizam un [emulator al sistemului de operare MS-DOS pentru x86.](https://www.dosbox.com/) In completare, avem nevoie de un asamblor, un link-editor si optional un debugger.

#### Asambloare: <a href="#asambloare" id="asambloare"></a>

Un asamblor, în contextul programării la nivel de limbaj de asamblare, este un program software care traduce codul scris în limbaj de asamblare (o formă de cod de nivel scăzut, apropiat de limbajul mașinii, dar lizibil pentru om) în limbajul mașinii (cod binar executabil de către un procesor). Rolul principal al asamblorului este de a facilita scrierea programelor prin oferirea unui mediu unde instrucțiunile pot fi exprimate prin mnemonici (scurtături lizibile) și simboluri, în loc de cod binar sau hexazecimal pur. Asamblorul se ocupă de detaliile complexe ale codului mașină, inclusiv de calculul adreselor, rezolvarea simbolurilor și etichetelor, și formatul specific instrucțiunilor, permițând programatorilor să se concentreze asupra logicii și structurii programului. În esență, asamblorul servește ca un strat intermediar între programarea de nivel înalt și hardware-ul specific al computerului, făcând posibilă scrierea de software eficient și optimizat pentru o anumită arhitectură de procesor.

#### Linkere: <a href="#linkere" id="linkere"></a>

Un link editor, cunoscut mai des ca un linker, este un instrument esențial în procesul de dezvoltare a software-ului, care joacă un rol diferit și complementar față de un asamblor. După ce un asamblor convertește codul sursă din limbaj de asamblare în cod mașină (generând fișiere obiect), linker-ul intervine pentru a combina aceste fișiere obiect cu bibliotecile necesare pentru a crea un singur fișier executabil. Rolul principal al linker-ului este de a rezolva referințele și legăturile dintre diferitele module ale programului (cum ar fi funcții și variabile) și a le uni într-un format specific, care poate fi executat de sistemul de operare.

Linker-ul gestionează adresele relative și absolute, ajustându-le astfel încât să corespundă locațiilor reale de memorie unde vor fi încărcate când programul este executat. De asemenea, poate optimiza codul prin eliminarea codului neutilizat și poate gestiona diferite secțiuni ale programului, cum ar fi secțiunea de cod, secțiunea de date și stiva. Prin aceste procese, linker-ul facilitează transformarea codului sursă și a bibliotecilor într-o formă finală, gata de lansare și execuție pe un sistem specific. În esență, linker-ul face posibilă construirea de programe complexe, modulare, care pot include cod din multiple surse și biblioteci.

#### Debuggere: <a href="#debuggere" id="debuggere"></a>

Un debugger permite programatorilor să inspecteze și să modifice starea unui program în timpul execuției, facilitând identificarea și corectarea erorilor (sau "bug-urilor").

**Funcționalități Cheie ale unui Debugger de Asamblare:**

1. **Breakpoints**: Debugger-ul permite setarea de breakpoints, puncte în cod unde execuția se oprește temporar, astfel încât programatorii să poată examina starea curentă a programului. Acest lucru este crucial pentru a înțelege cum evoluează starea programului până la un anumit punct.
2. **Vizualizarea Registrilor și Memoriei**: Oferă posibilitatea de a vizualiza și modifica conținutul registrilor procesorului și al memoriei. Acest lucru este esențial pentru a înțelege cum instrucțiunile de asamblare afectează starea mașinii.
3. **Execuție Pas cu Pas**: Permite execuția linie cu linie (single-step execution) a codului, ceea ce ajută la înțelegerea exactă a fiecărei instrucțiuni și a impactului acesteia.
4. **Analiza Stack-ului**: Poate oferi o vedere detaliată a stack-ului de execuție, ajutând la urmărirea apelurilor de funcții și a gestionării datelor.
5. **Identificarea Excepțiilor și Erorilor**: Ajută la identificarea și diagnosticarea excepțiilor, cum ar fi accesul ilegal la memorie sau erorile de diviziune prin zero.

**Importanța utilizarii unui debugger:**

În programarea în limbaj de asamblare, unde controlul asupra mașinii este extrem de granular și detaliat, un debugger este vital. Fără un debugger, ar fi extrem de dificil să urmăriți și să înțelegeți comportamentul unui program de asamblare, în special atunci când lucrați cu operații complexe de nivel scăzut și interacțiunea directă cu hardware-ul. Debugger-ele oferă o perspectivă necesară pentru a scrie cod de asamblare eficient și corect.

#### Instrumentele utilizate: <a href="#instrumentele-utilizate" id="instrumentele-utilizate"></a>

* Pentru asamblor, vom utiliza TASM (Turbo Assembler)
* Pentru linker, vom utiliza TLINK (Turbo Linker)
* Pentru debugger vom utiliza TD (Turbo Debugger)
* Drept emulator, vom utiliza DOS-BOX

{% hint style="info" %}
Toate aceste instrumente pot fi disponibile pre-instalate cu ajutorul extensiei de Visual Studio Code: [**MASM/TASM**.](https://marketplace.visualstudio.com/items?itemName=xsro.masm-tasm)
{% endhint %}

### Exemplu executie normala:

<figure><img src="/files/sKmRjE8Y6WzaSoYXJ2xo" alt=""><figcaption><p>click dreapta pe codul sursa in assembly si apoi "Run ASM code"</p></figcaption></figure>

<figure><img src="/files/HIWlZnaKRyY7y0xV9xAD" alt=""><figcaption><p>rezultatul executiei programului</p></figcaption></figure>

De remarcat ca optiunea "Run ASM Code" realizeaza automat procesul de asamblare, link-editare si executa programul rezultat in urma acestor operatii.

### Exemplu de executie in modul debugging:

<figure><img src="/files/bxVXmnhGtqlfZliu3h9g" alt=""><figcaption><p>click dreapta pe codul sursa in assembly si apoi "Open Emulator"</p></figcaption></figure>

<figure><img src="/files/GDououIqJhnCTFP3nWmX" alt=""><figcaption><p>optiunea deschide emulatorul DOS-BOX, in linie de comanda</p></figcaption></figure>

Pentru a putea executa programul, acesta trebuie generat plecand de la fisierul cod sursa, prin procesul de asamblare, respectiv link-editare.

De notat, ca desi fisierul exemplu se numeste in VS Code `hello.asm`,  in emulator el va fi disponibil sub numele `test.asm`.

* Pentru asamblare se invoca din linie de comanda: `TASM test.asm`
* Pentru link-editare, se invoca din linie de comanda: `TLINK test.obj`
* <mark style="color:yellow;">\[OPTIONAL]</mark> Pentru a vedea fisierele din directorul de lucru, se invoca: `DIR`. Observam acum existenta fisierului `test.exe`
* Pentru executia normala, se invoca simplu: `test.exe`
* Pentru executia in modul debugging, se invoca: `TD test.exe`

<figure><img src="/files/yh5n9X95vd0vCzYisgns" alt=""><figcaption><p>executia in modul debugging, dupa executia primei instructiuni</p></figcaption></figure>

In imaginea de mai sus, se observa interfata debuggerului, cu sectiuni specifice:

* continutul segmentului de cod, prefixul `cs:` urmat de adresa instructiunii
* continutul segmentului de date, prefixul `ds:`
* continutulul segmentului de stiva, prefixul `ss:`
* valoarea fiecarui registru: `ax`, `bx`, ... `ip`
* valoarea flag-urilor din Registrul Indicatorilor de Stare: `c` carry, `z` zero flag, `s`, ... `d`

Registrii evidentiati cu alb sunt cei ale caror valori au fost modificate de instructiunea precedenta. In cazul de fata:

* registrul `AX` a devenit `0x087D` (adresa segmentului unde sistemul de operare a alocat variabilele. In codul nostru aveam referinta `@data` care a fost inlocuita cu valoarea explicita in procesul de asamblare / linke-editare)
* registrul `IP` (Instruction Pointer) s-a actualizat cu adresa urmatoarei instructiuni care va fi executata.
* executia instructiune cu instructiune se realizeaza prin apasarea succesiva a tastei `F8`

## Citirea si afisarea unui numar

Citirea unui numar de la tastatura poate fi rezolvata prin mai multe metode. Se pleaca de la presupunerea ca interactiunea se realizeaza prin introducerea cifra cu cifra, urmat de apasarea tastei `ENTER`.

## Algoritm construire numar cifra cu cifra:

In continuare, se propune urmatorul algoritm:

```asm6502
;construirea unui numar cifra cu cifra
initializare numar cu 0

citireCifra:
    citire caracter de la tastatura
    comparare cu Enter
        daca se confirma, am terminat de construit numarul
        daca nu se confirma:
            se transforma caracterul din ASCII in cifra
            se inmulteste numarul cu 10
            se aduna cifra
            numarul devine rezultatul adunarii de mai sus
repet citireCifra

amTerminatDeConstruit:
;valoarea matematica se gaseste in "numar"
```

## Algoritm afisare numar:

Pentru afisarea unui numar stocat intr-o variabila sau registru, se propune urmatorul algoritm:

```asm6502
initializare numarCifre cu 0

descompunereInCifre:
    impart numarul la 10
    ;restul impartirii reprezinta ultima cifra a numarului
    restul se pune in stiva
    numarCifre creste cu 1
    numarul devine catul impartirii
    comparare cat cu 0
        daca se confirma, am terminat de descompus in cifre
        daca nu se confirma, repet descompunereInCifre
        
afisareCifre:
    se extrage varful stivei
    se transforma din cifra in codul ASCII
    se afiseaza caracterul
repet afisareCifre ;de atatea ori cat valoarea numarCifre

```

## Implementare algoritmi:

Propunere implementare in Assembly x86, TASM:

{% code overflow="wrap" lineNumbers="true" %}

```asm
.model small
.stack 100h
.data
    numar dw 0
    ;numar indica o variabila de 32 biti, DW: define word
    ;variabila va fi initializata cu 0, pe 16 biti
.code
    start:
        mov ax, @data
        ;in ax vom avea adresa segmentului de memorie
        ;unde au fost alocate variabilele
        mov ds, ax

        mov cx, 10
        xor bx, bx
        ;echivalent cu "MOV BX, 0"
        ;dar mai eficient pentru ca nu lucreaza cu memoria

        citireCifra:
            mov ah, 01h
            int 21h
            
            cmp al, 13
            ;comparam cu codul ASCII al caracterului "Carriage Return"
                je numarCitit
            ;daca se confirma, inseamna ca am terminat de introdus numarul
            ;JE (jump if equal) face un salt conditionat la eticheta numarCitit

            sub al, 48
            ;se obtine valoarea matematica a cifrei din codul ASCII
            mov bl, al
            ;pastram o copie a cifrei, pentru ca AX va fi modificat
            ;in operatia de inmultire
            mov ax, numar
            mul cx
            ;MUL: inmultirea numerelor pozitive
            ;CX are 16 biti, inseamna ca MUL CX va inmulti AX cu CX
            ;si va stoca rezultatul pe 32 de biti: DX:AX
            add ax, bx
            mov numar, ax
            jmp citireCifra
            ;JMP: salt neconditionat

        numarCitit:

        mov bx, 10
        xor cx, cx ;numar cifre
        mov ax, numar
        inc ax
        ;exemplu operatie intre citire numar si afisare numar

        descompunere:
            xor dx, dx
            div bx
            ;desi numarul se afla practic in AX
            ;DIV BX, imparte DX:AX la BX
            ;deci trebuie sa ne asiguram ca DX = 0
            push dx
            ;punem ultima cifra in varful stivei
            inc cx
            ;incrementam numarul de cifre
            cmp ax, 0
                je afisareNumar
            jmp descompunere

        afisareNumar:
            pop dx
            ;extragem din stiva
            add dx, 48
            ;transformam din cifra in caracter ASCII
            mov ah, 02h
            int 21h
            ;invocarea intreruperii cu codul 02H
            ;adica afisarea caracterului al carui cod este in DL
        loop afisareNumar

        mov ah, 4ch
        int 21h
    end start
```

{% endcode %}

## Reutilizarea codului

Procedurile, adesea numite și subrutine sau funcții în alte limbaje de programare, sunt blocuri de cod care efectuează o sarcină specifică și pot fi apelate de oriunde din program.

#### Caracteristicile Procedurilor în TASM:

1. **Modularizare**: Procedurile permit împărțirea programelor complexe în unități mai mici și mai gestionabile, facilitând astfel atât scrierea, cât și întreținerea codului.
2. **Reutilizare**: Odată definită, o procedură poate fi apelată de mai multe ori din diferite părți ale programului, ceea ce îmbunătățește reutilizabilitatea codului.
3. **Structură**: Procedurile au o structură definită, începând cu un etichetă (numele procedurii) și terminând cu o instrucțiune de return (`RET` în TASM).
4. **Utilizarea stivei:** Procedurile folosesc adesea stiva pentru a stoca adresele de returnare și pentru a transmite parametrii între diferite părți ale programului.

## Citire si afisare numar cu proceduri, fara variabile

{% code overflow="wrap" lineNumbers="true" %}

```asm
.model small
.stack 100h
.data
.code
    citireNumar PROC ;numarul va fi construit in ax
        mov ax, @data
        mov ds, ax

        mov cx, 10
        xor bx, bx ;echivalent cu mov bx, 0;
        push bx

        citireCifra:
            mov ah, 01h
            int 21h
            cmp al, 13
                je numarCitit

            sub al, 48
            mov bl, al
            pop ax
            mul cx
            add ax, bx
            push ax
            jmp citireCifra

        numarCitit:
            pop ax
            ret
    citireNumar ENDP

    afisareNumar PROC
        mov bx, 10
        xor cx, cx ;numar cifre

        descompunere:
            xor dx, dx
            div bx
            push dx
            inc cx
            cmp ax, 0
                je afisareCifre
            jmp descompunere

        afisareCifre:
            pop dx
            add dx, 48
            mov ah, 02h
            int 21h
        loop afisareCifre
        ret
    afisareNumar ENDP

    main:
        ;INCLUDE citire-scriere.asm
        ;intr-un mediu MS-DOS real, codul poate fi structurat in mai multe fisiere

        mov ax, @data
        mov ds, ax
        
        call citireNumar
        ;apelul procedurii citireNumar
        ;numarul citit se returneaza in AX

        inc ax

        call afisareNumar ;se afiseaza ce e in AX

        mov ah, 4ch
        int 21h
    end main
```

{% endcode %}

### Sugestii de exercitii:

* extindeti procedurile de citire si afisare, astfel incat sa permita introducerea de la tastatura, respectiv afisarea la consola a numerelor negative (in formatul ex:  -92).
* extindeti procedura de citire astfel incat sa afiseze un mesaj de eroare in cazul in care utilizatorul introduce alte caractere decat "-", 0..9, Enter.

{% hint style="info" %}
În arhitecturile x86 de 16 biți, precum cea a procesorului 80286, stiva reprezintă o zonă de memorie importantă pentru salvarea temporară a datelor, adresei de revenire și a variabilelor locale în timpul execuției programului.

Stiva este localizată în segmentul indicat de registrul SS (Stack Segment), iar poziția curentă din stivă este gestionată prin intermediul registrului SP (Stack Pointer). Operații elementare precum `PUSH` și `POP` permit împingerea și extragerea datelor din stivă, de exemplu `PUSH AX` depozitează conținutul lui AX în stivă și decrementează SP cu 2, iar `POP BX` extrage un cuvânt (16 biți) din stivă în BX și crește SP cu 2.

Dincolo de aceste operațiuni de bază, este frecventă folosirea registrului BP (Base Pointer) pentru a accesa variabile locale și argumente ale funcțiilor stocate pe stivă. Într-o subrutină, la intrare, se obișnuiește să se salveze vechiul BP (`PUSH BP`), apoi să se seteze BP la nivelul curent al stivei (`MOV BP, SP`), astfel încât variabilele locale și argumentele pot fi accesate prin adrese de forma `[BP - offset]` pentru variabile locale și `[BP + offset]` pentru argumente. Spre exemplu, dacă o funcție primește un argument pe stivă, valoarea acestuia poate fi citită în registrul AX cu instrucțiunea `MOV AX, [BP+4]`, presupunând că primele 2 octeți de deasupra lui BP sunt adresa de revenire și următorii 2 octeți reprezintă argumentul respectiv. În mod similar, variabile locale pot fi create prin decrementarea SP și apoi accesate relativ la BP, ca în `MOV [BP-2], AX`, pentru stocarea conținutului lui AX într-o variabilă locală.

Astfel, stiva joacă un rol esențial în gestionarea structurii interne a subrutinelor, oferind o metodă flexibilă și sistematică de lucru cu datele temporare și argumentele funcțiilor.
{% endhint %}

#### Exemplu de lucru cu stiva:

```asm
.MODEL SMALL
.STACK 100h

.DATA
result dw ?

.CODE
start:
    ; Inițializare segment de date
    mov ax, @DATA
    mov ds, ax

    ; Punem argumentele pe stivă în formă hexadecimala standard TASM
    push 20h    ; al doilea argument (32 în decimal)
    push 10h    ; primul argument (16 în decimal)
    call sum

    ; AX conține rezultatul, îl stocăm în memorie
    mov result, ax

    ; Terminare program DOS
    mov ah, 4Ch
    int 21h

; Subrutina sum(a, b):
; La intrarea în subrutină:
;   [BP+4] = primul argument (a)
;   [BP+6] = al doilea argument (b)
; Rezultat în AX = a + b
sum proc
    push bp
    mov bp, sp
    sub sp, 2            ; rezervăm 2 octeți pentru o variabilă locală (la [BP-2])

    mov ax, [bp+4]       ; AX = a
    add ax, [bp+6]       ; AX = a + b
    mov [bp-2], ax       ; variabilă locală = a+b

    mov ax, [bp-2]       ; AX = a+b

    mov sp, bp           ; eliberăm spațiul alocat local
    pop bp
    ret 4                ; revenim și curățăm argumentele de pe stivă (4 octeți)
sum endp

END start

```

## \[EXTRA] utilizarea coprocesorului (80287)

#### Lista instructiunilor pentru FPU (Floating Point Unit):

{% embed url="<http://www.techhelpmanual.com/876-80x87_floating_point_opcodes.html>" %}

{% code overflow="wrap" lineNumbers="true" %}

```asm
.model small
.stack 200h
.data
    value1  DD  3.14
    value2  DD  2.71
    result  DD  ?

.code
start:
    mov ax, @data
    mov ds, ax

    FINIT               ; Initialize the coprocessor
    FLD     value1      ; Load the first value
    FLD     value2      ; Load the second value
    FADD                ; Perform addition: ST(0) = ST(0) + ST(1)
    FSTP    result      ; Store the result in 'result'
    ; the result should be 0x40bb3333

    mov     ax, word ptr result     ; Move lower 16 bits of result into AX
    mov     bx, word ptr result + 2 ; Move upper 16 bits of result into BX

    mov ah, 4ch
    int 21h

end start
```

{% endcode %}

## Instrumente si linkuri utile

### Lista instructiuni:

{% embed url="<http://www.techhelpmanual.com/866-8088_286_386_486_pentium_instruction_set.html>" %}

### Extensie Visual Studio Code pentru emularea MS-DOS si TASM:

{% embed url="<https://marketplace.visualstudio.com/items?itemName=xsro.masm-tasm>" %}

### Lista apelurilor de sistem MS-DOS (intreruperi int 21h):

{% embed url="<http://bbc.nvg.org/doc/Master%20512%20Technical%20Guide/m512techb_int21.htm>" %}

{% embed url="<http://www.techhelpmanual.com/23-dos_function_index___by_number.html>" %}

### Operatii aritmetice:

{% embed url="<https://www.tutorialspoint.com/assembly_programming/assembly_arithmetic_instructions.htm>" %}

### Vizualizare proces fabricatie:

{% embed url="<https://ig.ft.com/microchips/>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://mihai-gheorghe.gitbook.io/bti-suport-curs/07-introducere-in-assembly.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
