Beta [ Pagina Principale || Sommario || Redazione || Informazioni || Beta Browser ]

[Rubrica Hardware Logo]

coordinamento di Fernando Carello



La CPU e il resto del mondo...

di Francesco Sileno

Nota: in questo articolo ci sono dei riferimenti ipertestuali a termini tecnici definiti nei precedenti Numeri di Beta. Nel collegare questi argomenti, si è assunto che la directory del Numero 2 sia "../0295", del Numero 3 sia "../0395" e così via per gli altri Numeri. Per esempio una struttura tipica potrebbe essere c:\beta\0295, c:\beta\0395, ecc., prendendo come nome di directory il numero contenuto nel nomefile di Beta, oppure nel file di revisione.
I riferimenti sono attivi solo per installazione in locale.


In vista di una serie di articoli dedicati all'assembly, vi riassumo qui di seguito, in maniera rapida e indolore, il funzionamento della CPU, la sua struttura e il modo in cui si interfaccia con le periferiche. Lascio il compito di spiegare il funzionamento dei singoli segnali di strobe e sincronismo al collega Fernando, che ringrazio comunque per l'assistenza prestatami.
L'architettura di cui mi occuperò sarà quella adottata dai cloni IBM compatibili, probabilmente la più diffusa, sicuramente l'unica su cui mi possa permettere di dissertare.


CPU

La CPU, il cuore del computer. Il suo unico scopo è eseguire ininterrottamente istruzioni, sempre. Persino quando il PC si inchioda, la CPU continua a eseguire istruzioni, anche se sono istruzioni senza più alcun senso.
Al suo interno vi sono un ALU (Aritmetic Logic Unit), programmata in logica cablata, che si occupa di svolgere i calcoli matematici (non parlo del coprocessore, la ALU lavora solo su interi), una CU (Control Unit), che 'capisce' le istruzioni che arrivano al processore e le 'esegue', una serie di registri e buffer ad uso interno, dei registri ad uso di tutti, e nelle versioni più recenti alcuni integrati di controllo, cache e coprocessore.

I Registri

Il registro è una componente della CPU in grado di memorizzare a tempo intederminato un valore, di dimensione pari alla parola che il processore stesso è in grado di elaborare. Ad ogni registro è associato un nome mnemonico, ed è tramite tale nome che si può accedere al suo contenuto.
Noi prendiamo in considerazione solo quelli in qualche modo accessibili all'utente, e rimaniamo a quelli compatibili 8086 (per ora). Accanto ad ognuno di essi la descrizione e un indicazione sull'uso che se ne fa in generale.

Registri Segmento

ES - Extra Segment - Dati
DS - Data Segment - Dati
CS - Code Segment - Codice
SS - Stack Segment - Stack

Registri Offset

IP - Instruction Pointer - Codice
SP - Stack Pointer - Stack
BP - Base Pointer - Stack/Dati
DI - Destination Index - Dati
SI - Source Index - Dati

Registri di uso generale

AX - Accumulator
BX - Base
CX - Counter
DX - Destination

CS:IP - Punta alla prossima istruzione da dover eseguire
SS:SP - Punta alla testa dello stack
Con r1:r2 intendo dire che per sapere l'indirizzo effettivo si devono tenere in considerazione entrambi i registri, vedremo poi in che modo.

Registro Flag

Questo registro è un po' particolare. Alcuni suoi bit sono usati per rappresentare determinate situazioni, e nonostante non sia visibile dall'utente ci sono istruzioni che permettono di controllarne i singoli bit.
Bit: 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
Flag: OF DF IF TF SF ZF AF PF CF
OF - Overflow
Owerflow aritmetico, quando la dimensione del risultato di un'operazione supera la dimensione del registro/locazione di destinazione.

DF - Direction
Indica se le istruzioni per uso su stringhe causano un auto-incremento o un auto-decremento dei registri indice.

OF - Interrupt Enable
Quando questo flag è a 0, nessun interrupt, tranne i non mascherabili (NMI), può essere generato.

TF - Trap
Usato dai debugger, se il flag è attivo, dopo ogni istruzione viene generato in INT 3

SF - Sign
0 per numeri positivi, 1 per numeri negativi.

ZF - Zero
Se il risultato dell'operazione è 0, il flag è attivo.

AF - Auxiliary Carry
Usato per istruzioni ad aritmetica-decimale, senga resti prestiti intermedi.

PF - Parity Flag
Se il numero di bit con valore pari a 1 è pari nel risultato, il flag è attivo.

CF - Carry Flag
Indica la presenza di riporto o di prestito nel risultato. Utile per numeri multi-word.

I Bus

Se ci fosse solo la CPU, sarebbe un ben misero guadagno la possibilità di fargli fare milioni di operazioni al secondo. Per questo è stata fornita di una serie di canali per comunicare e interagire con l'esterno.
Tre sono le vie di comunicazione:
DATA BUS
costituito da 2^N linee fisiche, qui sopra viaggiano i dati in ingresso e in uscita. Un registro interno, il Data Register (DR), mentiene l'ultima parola transitata sul bus.
ADDRESS BUS
2^M linee, l'informazione che questo bus trasmette è l'indirizzo della locazione di memoria (ma non solo) che si intende leggere o scrivere, ed è unidirezionale, dalla CPU al mondo. Il registro che mantiene l'indirizzo impostato è l'AR (Address Register).
CONTROL BUS
un insieme di linee che svolgono svariati compiti, raggruppati idealmente in un bus per comodità. In questo bus sono presenti ad esempio i segnali MEMR,MEMW,DRQ,IRQ, etc. Ad ogni segnale corrisponde un piedino della CPU, e la bidirezionalità o meno dipende dal segnale stesso.
La dimensione dell'ADDRESS BUS ci dice quante parole può effettivamente indirizzare la nostra CPU.

Facciamo un esempio: supponiamo una CPU con parola di 8bit, bus indirizzi di 16bit, che voglia leggere il byte precedentemente memorizzato all'indirizzo 534h. Quello che deve fare, in una versione estremamente semplificata e idealizzata, è:

CPU al Lavoro:

per ogni istruzione che la CPU esegue, si possono distinguere tre fasi di elaborazione:
  1. FETCH, in cui la CPU legge, nel modo che abbiamo visto in precedenza, la parola corrispondemte all'istruzione da eseguire, puntata da CS:IP. Quindi la coppia viene incrementata in modo da puntare alla prossima istruzione.
  2. DECODE, l'istruzione assembly viene tradotta in microcodice, ovvero in una serie di passi elementari a bassissimo livello da compiere. In genere la traduzione in microcodice avviene tramite tabelle HASH impresse nella CPU, ma alcune CPU (come quella che usa il VAX, se non erro), prevedono la possibilità di modificare anche il microcodice.
  3. EXECUTE, il microcodice viene eseguito, quindi si torna al punto 1.
Esempio:
1234:5678 mov ax,4C00 (B8004C)
1234:567B int 21 (CD21)

CS:IP vale 1234:5678.
  1. La CPU legge la parola B800, che viene posta in un registro interno (IR - Instruction Register). Quindi CS:IP vengono posti a 1234:567B.
  2. Decodificando l'istruzione la CPU si accorge che l'istruzione ha dei parametri, quindi genera la serie di passi che serviranno a:
  3. I passi sopra elencati vengono eseguiti, e si torna al punto 1.
In realtà le cose non avvengono esattamente così: anche le CPU più vecchie hanno una PREFETCH QUEUE, ovvero un piccolo buffer dove non caricano solo la parola puntata, ma anche un certo numero di quelle successive. Ma il discorso non cambia molto.

Segmentazione:

ovvero l'astruso modo inventato dai tecnici INTEL per accedere alla memoria, basato su un segmento e un offset. Gli 8086 potevano indirizzare al massimo 1MB. Perchè? Se si usasse un solo registro, IP, si avrebbero 16 linee di indirizzo, pari a 64k. D'altronde usandone due, CS:IP, sommati così:
        [CS]0000 +
            [IP] =
        ----------
        [CS][IP]
si avrebbero avute 32 linee di indirizzo, pari a 4Gb.
Evidentemente all'epoca sembrava uno spreco immane usare 32bit di indirizzo, quando Bill Gates affermava che 640kb sarebbero stati più che sufficenti per tutti. (Poi, quando lo stesso Bill inventò Windows, ripresero in considerazione i 32bit, e crearono il MODO PROTETTO). Allora fecero in questo modo
        [CS]0 +
        [IP] =
        -------
        20bit
la memoria indirizzabile fu 2^20 = 1Mb. Per questo motivo, la locazione 1234:5678 e la locazione 1000:79B8 sono la stessa di 1700:09B8. Tutte e tre hanno come indirizzo effettivo il numero 179B8, ricordatevene quando lavorate con segmenti e offset. Allo stesso modo, notate che un segmento non può superare la dimensione di 64Kb, in quanto l'offset è a 16bit. Questo tipo di indirizzamento viene usato quando la CPU si trova a lavorare in MODO REALE


PERIFERICHE

Quando costruirono la prima CPU, si resero conto che sarebbe stato carino avere un modo per dirgli cosa fare, e per sapere cosa ha fatto. Così cominciarono a inventarsi le schede perforate, i tastierini esadecimali, fino ad arrivare ai monitor, le tastiere, le schede sonore, le schede WinTv, etc... Ora, come fanno tutte queste cose a interagire con la CPU? Da quello che abbiamo visto finora, si potrebbe pensare di riservare un area di memoria alla periferca, ed in effetti qualcuna fa così. Ma questo, oltre a sottrarre indirizzi alla memoria centrale, non è sufficente. Quindi misero a disposizione della CPU altri metodi per chiacchierare con il mondo là fuori...

Porte di I/O:

sono ulteriori canali a 8bit, che permettono lo scambio di informazioni senza dover usare o rimappare della memoria. Possono essere uni o bi-direzionali, ad ogni porta della periferca corrisponde un registro o una locazione di una piccola memoria, che la periferica usa per scambiare dati o per configurarsi. Persino alcuni chip di controllo del PC, come il generatore di clock o il controller degli interrupt, hanno delle porte che permettono al bios (e a noi!) di riconfigurarli secondo i nostri gusti. Fate attenzione che spesso viene indicata soltanto la prima delle porte di I/O usate, sottintendendo che un certo numero di quelle immediatamente successive è a loro volta usato. La Sound Blaster, ha come porta base la 220h, ma dalla 220h alla 233h le usa tutte (SB16).

IRQ:

L'IRQ (Interrupt ReQuest) è un segnale che una periferica invia alla CPU per segnalare una particolare situazione, il terminato trasferimento o la disponibilità di dati. E' un segnale hardware, da non confondere con gli interrupt software, indicati con INT. Per ogni IRQ ricevuto la CPU genera un ben determinato INT:
IRQ INT IRQ INT
0 0x08 8 0x70
1 0x09 9 0x71
2 0x0A 10 0x72
3 0x0B 11 0x73
4 0x0C 12 0x74
5 0x0D 13 0x75
6 0x0E 14 0x76
7 0x0F 15 0x77

Sembrerà strano, ma si è presa l'abitudine di indicare gli IRQ in numerazione decimale, mentre gli INT vengono espressi in esadecimale.

Gli XT avevano a disposizione soli gli IRQ 0..7, dagli AT in poi sono diventati 15 (quasi).
Il fatto che in corrispondenza di un IRQ venga generato un INT, ci permette di poter installare su tale INT una routine di gestione della periferca.

DMA:

Il DMA (Direct Memory Access) permette un trasferimento direttamente con la memoria del PC, scavalcando la CPU, tramite un canale dedicato. L'integrato controllore del DMA si impossessa del bus dati e bus indirizzi ed effettua lo spostamento, rilasciandoli per brevi intervalli alla CPU in modo da non bloccare del tutto il sistema. Al termine del trasferimento la periferca genera un IRQ, che avvisa la CPU del completamento dell'operazione. Come sopra, gli XT avevano 4 canali DMA (0..3), poi ne misero 8.

Considerazioni:

Anzitutto, vediamo con una breve tabella quali sono i modi usati dalle principali periferiche del PC:
I/O IRQ DMA
Tastiera SI SI NO
Monitor SI [1] NO
Floppy SI SI SI
HD SI SI SI(opt.)
Int. Seriale SI SI NO
Int. Parallela SI SI SI[2]
Scheda Sonora SI SI SI
CD-ROM non IDE SI SI SI(opt.)
Scheda di rete SI SI SI(opt.)
Scanner SI SI SI
[1] Alcune schede grafiche EGA fanno uso di un IRQ per avvisare l'avvenimento di un retrace dello schermo.
[2] Le nuove interfacce parallele ECP permettono il traferimento tramite DMA.

Buffo notare che i floppy hanno da sempre usato il trasferimento DMA, al contrario degli HD che pure hanno un loro canale dedicato. Quando entra in gioco il trasferimento di grandi quantità di dati, o quando è importante la velocità con cui i dati devono essere trasferiti, la differenza nell'usare il trasferimento tramite porte di I/O o quello tramite DMA diventa notevole.
Nel trasferimento con porte di I/O, la CPU deve leggere/scrivere un byte alla volta, entrando in un loop che termina col trasferimento. Gli svantaggi che si hanno sono:

D'altra parte un trasferimento tramite DMA comporta un lieve rallentamento del sistema, poichè soltanto un dispositivo alla volta può accedere ai bus di indirizzi e dati, ma sempre in maniera decisamente minore.

Un esempio pratico: il treferimento da/per memorie di massa quali gli Hard Disk. Il metodo usato finora col DOS è il PIO, Polled I/O, ovvero quello che fa uso delle porte di I/O. Per accellerare la capacità di trasferimento dell'HD, si sono diminuiti i tempi necessari al singolo byte per essere disponibile sulla porta, introducendo i vari PIO2, PIO3 e PIO4. Finchè si sta in DOS, la cosa sembra essere meravigliosa. Non appena si passa a sistemi multitask, ogni trasferimento di questo tipo rallenta notevolmente tutto il sistema, e fortunatamente il recente diffondersi di sistemi operativi multitasking (parlo di quelli veri) ha convinto i produttori di hardware a rendere disponibile il trasferimento DMA anche per gli HD. Con gioia di tutti noi, e con rabbia di chi usa gli SCSI, che ora non ha più questo motivo per sfottere (ne ha sempre molti altri, però).
Per maggiori dettagli su questo argomento, fate riferimento all'articolo di Fernando apparso sul numero 3 di Beta.

Bene, a questo punto ne sapete quanto può bastare per cominciare a lavoricchiare. Un poco alla volta scenderemo nei dettagli, abbiate pazienza.

                                                         perifericamente,
                                                              Cthulhu
                                                              
Copyright © 1996 Beta Working Group. Tutti i diritti riservati.
Beta [ << || Pagina Principale || Sommario || Redazione || Informazioni || Beta Browser || >> ]