# Assembly ## Introduzione Nei computer, le CPU possono basarsi su diverse architetture, ognuna con il proprio modo di interpretare e gestire le istruzioni. Le architetture più comuni includono **CISC** (Complex Instruction Set Computing), che utilizza un ampio set di istruzioni complesse, e **RISC** (Reduced Instruction Set Computing), che privilegia un set di istruzioni più semplice e standardizzato per ottimizzare la velocità di esecuzione. In questa sezione, esploreremo un’architettura semplice come quella RISC, che permette di comprendere chiaramente come funzionano le istruzioni e i registri. ## I registri della CPU In un’architettura semplice, il processore dispone solitamente di **32 registri generali** di grandezza **32 bit** (4 byte), utilizzati per contenere temporaneamente dati durante l’esecuzione delle operazioni. I registri sono piccole unità di memoria interne alla CPU, estremamente veloci, che permettono di caricare, memorizzare e manipolare rapidamente i dati necessari. Alcuni registri hanno funzioni specifiche; uno dei più importanti è il **Program Counter (PC)**, che contiene l’indirizzo della prossima istruzione da eseguire. Il PC permette alla CPU di mantenere la posizione corrente nel programma e avanzare alla prossima istruzione a ogni ciclo di fetch-decode-execute. ## Struttura delle istruzioni In un’architettura di tipo RISC, ogni istruzione è composta da **32 bit** suddivisi in campi specifici, ognuno con un ruolo preciso. La suddivisione tipica dei 32 bit è: - **6 bit** per l’**opcode**: identifica l’operazione da eseguire (ad esempio, somma, carica, confronto). - **5 bit** per il **registro di destinazione**: specifica dove salvare il risultato. - **5 bit** per il **primo registro sorgente**: indica uno dei registri da cui prendere i dati. - **5 bit** per il **secondo registro sorgente** o **registro base**: indica un secondo registro per operazioni aritmetiche o logiche oppure il registro base per accesso alla memoria. - **11 bit** rimanenti: utilizzati per valori immediati o offset, necessari in operazioni come salti condizionati o accesso alla memoria. ## Esempio di istruzioni e suddivisione dei bit Vediamo ora alcuni esempi di istruzioni e come sono strutturati i loro 32 bit. Ogni esempio include una tabella che mostra la suddivisione dei bit. ### 1. Esempio: **ADD R1, R2, R3** Questa istruzione somma i valori contenuti nei registri **R2** e **R3** e salva il risultato nel registro **R1**. La struttura dei bit è la seguente: | Campo | Bit | Descrizione | |----------------|---------------|-------------------------------------| | Opcode | 6 bit | Codice dell’operazione (ADD) | | Destinazione | 5 bit | Registro di destinazione (R1) | | Registro Sorgente 1 | 5 bit | Primo registro sorgente (R2) | | Registro Sorgente 2 | 5 bit | Secondo registro sorgente (R3) | | Non utilizzati | 11 bit | Non utilizzati per questa istruzione| **Rappresentazione in binario** (esempio): - **Opcode (ADD)**: `000001` - **R1**: `00001` - **R2**: `00010` - **R3**: `00011` - **Non utilizzati**: `00000000000` **Totale**: `000001 00001 00010 00011 00000000000` ### 2. Esempio: **LOAD R1, 16(R2)** Questa istruzione, **LOAD R1, 16(R2)**, esegue un’operazione di caricamento (o **load**) di un valore dalla memoria RAM al registro **R1**. Vediamo in dettaglio come funziona e perché un’operazione di questo tipo è importante. In molti programmi, i dati che il processore deve elaborare sono troppo numerosi per essere memorizzati interamente nei registri, che sono pochi e di dimensione limitata. Per questo motivo, i dati sono spesso conservati nella **memoria di lavoro** (RAM), una memoria più grande ma più lenta rispetto ai registri. Tuttavia, per elaborare questi dati, la CPU deve caricarli nei registri, ed è qui che entra in gioco un’istruzione come **LOAD**. #### Cosa fa l'istruzione LOAD R1, 16(R2)? L’istruzione **LOAD R1, 16(R2)** specifica alla CPU di: 1. **Calcolare un indirizzo di memoria** sommando l’**offset** 16 al valore contenuto nel registro **R2**. In questo contesto, il registro **R2** è usato come **registro base**, il che significa che contiene un indirizzo di memoria di riferimento. L’offset 16 indica una posizione di memoria relativa a questo indirizzo base. 2. **Accedere alla memoria all’indirizzo calcolato** e leggere il valore presente in quella posizione. 3. **Copiare il valore letto dalla memoria** nel registro **R1**, che fungerà da spazio di lavoro temporaneo in cui la CPU può manipolare i dati. | Campo | Bit | Descrizione | |-----------------|-------------|-------------------------------------------| | Opcode | 6 bit | Codice dell’operazione (LOAD) | | Destinazione | 5 bit | Registro di destinazione (R1) | | Registro Base | 5 bit | Registro base per l’indirizzo (R2) | | Offset | 16 bit | Valore immediato o offset (16) | **Rappresentazione in binario** (esempio): - **Opcode (LOAD)**: `000010` - **R1**: `00001` - **R2**: `00010` - **Offset (16)**: `0000000000010000` **Totale**: `000010 00001 00010 0000000000010000` ### 3. Esempio: **BEQ R1, R2, 8** Questa è un’istruzione di salto condizionale. La CPU verifica se i valori di **R1** e **R2** sono uguali. Se lo sono, la CPU salta a un’istruzione situata a **8** posizioni di distanza. Questo significa che se i due valori sono uguali l'esito dell'operazione sarà l'incremento del *Program Counter* di 8 unità. La struttura dei bit è: | Campo | Bit | Descrizione | |-----------------|-------------|-------------------------------------------| | Opcode | 6 bit | Codice dell’operazione (BEQ) | | Registro 1 | 5 bit | Primo registro per confronto (R1) | | Registro 2 | 5 bit | Secondo registro per confronto (R2) | | Offset | 16 bit | Offset per il salto (8) | **Rappresentazione in binario** (esempio): - **Opcode (BEQ)**: `000011` - **R1**: `00001` - **R2**: `00010` - **Offset (8)**: `0000000000001000` **Totale**: `000011 00001 00010 0000000000001000` ### Riassunto tabellare delle istruzioni di esempio | Istruzione | Opcode | Destinazione | Sorgente 1 | Sorgente 2 / Base | Offset/Immediato | |------------------------|--------|--------------|------------|-------------------|------------------------| | **ADD R1, R2, R3** | `000001` | `00001` | `00010` | `00011` | `00000000000` (11 bit) | | **LOAD R1, 16(R2)** | `000010` | `00001` | `00010` | - | `0000000000010000` | | **BEQ R1, R2, 8** | `000011` | `00001` | `00010` | - | `0000000000001000` | --- ## Conclusione In un’architettura di tipo RISC, ogni istruzione occupa **sempre 32 bit** e segue una **struttura regolare e uniforme**. Questo design rende più veloce e prevedibile la decodifica delle istruzioni, consentendo alla CPU di eseguire un numero elevato di operazioni per secondo.