sabato 25 dicembre 2010

Print
Dopo un po' di articoli leggeri dedicati ai videogiochi, è giunto il momento di tornare a parlare un po' di hardware andando ad analizzare la seconda generazione di processori della famiglia x86. Quindi bando alle ciance e proseguiamo la nostra cavalcata in sella al cuore pulsante dei nostri amati PC ...

80186

All'inizio del 1982 la famiglia x86 di Intel si arricchisce di un nuovo microprocessore, il misconosciuto 80186 che, nonostante alcune interessanti innovazioni, fu relegato ben presto al solo mercato dei microcontrollori (processori single-chip generalmente utilizzati all'interno di sistemi embedded per applicazioni specifiche di controllo digitale).

L'80186 comodamente ospitato suo case PLCC a 68-pin


La nuova CPU Intel deriva direttamente dall'8086 da cui eredita il bus dati a 16 bit ed il bus indirizzi a 20 che gli permettono di accedere ad 1MB di memoria con lo stesso arzigogolato meccanismo già descritto per il capostipite dell'architettura x86. Le prime versioni dell'80186 commercializzate, in grado di operare alla frequenza di 6MHz, risultavano decisamente più performanti delle CPU di generazione precedente di pari frequenza, grazie alle numerose istruzioni codificate in hardware (calcolo degli indirizzi, operazioni di moltiplicazione e divisione, ecc).
Per ridurre il numero di chip esterni al processore, l'80186 integra al suo interno parte della logica necessaria al funzionamento, come illustrato nel seguente diagramma.



Il diagramma blocchi dell'architettura 80186 (click per ingrandire)

Come potete notare alcuni componenti come il generatore di clock, il controllore DMA ed il controllore delle interruzioni sono presenti direttamente all'interno del chip. Questa scelta favorì l'utilizzo dell'80186 come microcontrollore (essendo pratica in grado di funzionare con pochi elementi esterni) ma ne penalizzò l'uso all'interno dei personal computer, vista la sostanziale incompatibilità con l'architettura IBM PC costruita intorno all'8086. Nonostante questo "difetto" il nuovo processore Intel trovò spazio all'interno di alcuni sistemi entrati a loro modo nella storia; uno fra tutti il Tandy 2000, elaboratore commercializzato dalla catena di elettronica americana Radio Shack all'inizio del 1983, che grazie alla sua innovativa scheda grafica era in grado di fornire prestazioni di tutto rispetto per quei giorni.


Un giovanissimo Bill Gates posa accanto al Tandy 2000 con la primissima versione di Windows

Prima di passare oltre segnalo per dovere di cronaca l'80188, versione economica dell'80816 con bus dati a 8-bit.

80286

Il primo febbraio 1982 arriva sul mercato il nuovo processore di mamma Intel destinato a soppiantare l'ormai vetusto 8086 (datato 1978) nel sempre più effervescente mercato IBM PC. L'80286 condivide le stesse innovazioni architetturali del fratello minore che gli permettano di esibire prestazioni anche venti volte maggiori di un 8086 operante alla stessa frequenza di clock. Non vengono però riproposte le caratteristiche che resero l'80186 più simile ad un microcontrollore che ad un processore vero e proprio permettendo così ad IBM di definire la seconda generazione della sua architettura personal computer, denominata PC AT (Advance Technology) che venne commercializzata dal 1984 al 1987.



L'80286 in tutto il suo splendore

Tra le novità più interessanti abbiamo l'ampliamento del bus indirizzi che passa da 20 a 24 bit che permette al processore di indirizzare ben 16MB di memoria, una quantità decisamente considerevole per l'inizio degli anni '80. Lo spazio completo di indirizzamento non è però disponibile nella modalità reale, modalità ereditata direttamente dall'8086 che prevede indirizzi su 20 bit (accessibile tramite la tecnica della segmentazione di cui vi ho già parlato) ed conseguente tetto di 1MB, ma solamente nella nuova modalità protetta di cui vi parlerò tra poco. La modalità protetta, oltre a permettere l'accesso ad un maggior quantitativo di memoria, fornisce, a livello hardware, il supporto al multithreading caratteristica che favorì il diffondersi dei sistemi operativi unix like (come Venix, SCO Xenix) anche sulle piattaforme del duo IBM/Intel. A questo punto, il buon vecchio MS-DOS inizia a mostrare il fianco essendo in grado di operare unicamente in modalità reale e rigorosamente monothread. Nulla vieta ai programma MS-DOS di operare in modalità protetta a patto che sia installato un opportuno Dos Extender che colma le lacune di base del sistema operativo. Uno dei maggiori difetti dell'80286 fu l'impossibilità di "rientrare" dalla modalità protetta senza il riavvio, caratteristica che rese di fatto questa modalità poco sfruttata, almeno in ambito MS-DOS (discorso differente per i sistemi operativi che agivano completamente in modalità protetta, come unix, in cui questa limitazione di fatto sparisce) ... decisamente un smacco per gli amanti dei videogame che da questo avrebbero tratto molteplici vantaggi!

L'indirizzamento delle memoria nella modalità protetta

Come vi ho già accennato la modalità protetta permette alla CPU di accedere completamente ai 16 MB indirizzabili dal 286. Non si tratta però di una modalità di indirizzamento lineare (come quella adottata da alcuni concorrenti, leggi Motorola 68000) ma si passa dalla gestione a segmenti dell'8086 a quella a selettori. Andiamo a vedere di cosa si tratta evitando di scendere troppo nei tecnicismi pur cercando di essere il più esaurienti possibile ..

La prima cosa che balza all'occhio, come già era successo per l'8086, è che l'ampiezza del bus degli indirizzi è superiore a quella dei registri (che sono ovviamente a 16-bit), questo implica che anche in questo caso l'indirizzo "finale" sarà determinato dalla combinazione di due valori che definiscono uno la posizione base e l'altro il suo scostamento (offset) rispetto a quest'ultimo. Mentre nell'8086 l'operazione era decisamente semplice, si sommava il valore di uno dei registri di base moltiplicato per 16 (shift di 4 posizione verso sinistra) con il contenuto del corrispondente registro indice (con "l'imbarazzante" risultato che coppie di valori differenti generavano lo stesso indirizzo fisico), in questo le cose si complicano un pò soprattutto per via della possibilità di eseguire più thread contemporaneamente (beh ... non proprio contemporaneamente ma dedicando un po' di tempo all'esecuzione di ognuno dando la "sensazione" di concorrenza) che devono vivere "pensando" di avere tutte le risorse a loro disposizione.

    Nella modalità protetta i registri di segmento diventano dei selettori la cui struttura è rappresentata nella figura seguente:

    Il selettore

    I primi due bit (RPL, Requested Privilege Level) definiscono il livello di privilegio (tra i quattro disponibili) del selettore permettendo in questo modo di "proteggere" le porzioni di codice più importanti, come ad esempio il sistema operativo, da malevole "interferenze" (ad esempio dei programmi utente). Il terzo bit (TI, Table Indicator) indica a quale insieme appartiene il selettore: locale all'applicazione (LDT, Local Descriptor Table) o globale al sistema (GDT, Global Descriptor Table). I restanti 13 bit definiscono l'indice dell'elemento nella tabella dei descrittori (avremo quindi 8192 descrittori locali e altrettanti globali).
    A questo punto avrete capito che il nome selettore deriva dal fatto che tale valore viene utilizzato per selezionare un elemento all'interno delle tabelle dei descrittori, che sono il vero punto di partenza per il calcolo dell'indirizzo.

    I descrittori sono speciali strutture di memoria (grandi 8 byte, 64 bit) che descrivono un segmento. La loro struttura è la seguente (su ogni "linea" sono rappresentati 2 byte)

    Struttura di un descrittore di segmento
    • 2 byte (16 bit) quantità di byte a disposizione del segmento (LIMIT)
    • 3 byte (24 bit) di indirizzo di base del segmento (BASE)
    • 1 byte contente gli indicatori (flag) di controllo
    • 2 byte riservati per future estensione (già si pensava al 32 bit della futura generazione di processori)


     I flag di controllo del descrittore di segmento


    I flag di controllo sono i seguenti:
    •  A: Accessed, vale 1 se è stato effettuato accesso al segmento
    • TYPE: definisce il tipo di segmento (codice, dati, stack, ecc)
    • 1: Questo bit vale sempre uno per i descrittori di segmento (esistono altri tipi di descrittori ma li tralascio per non "sovraccaricarvi" troppo)
    • DPL: Descriptor Privilege Level, livello di privilegio richiesto per accedere al segmento
    • P: Present, vale 1 se il segmento è presente nella memoria fisica

    Per chiudere il cerchio ed effettuare la "procedura" di indirizzamento è necessario introdurre due nuovi registri GDTR (GDT Register) e LDTR (LDT Register) che contengono rispettivamente l'indirizzo della locazione di memoria in cui inizia la Global Descriptor Table e la Local Descriptor Table. La GDT è una ed è controllata, in genere, dal sistema operativo. Di LDT ne esistono più d'una in quanto sono, in genere, associate ad ogni singola applicazione in esecuzione (i puntatori alle varie LDT sono memorizzati nella GDT in speciali descrittori su cui non indugerò oltre, vi basti sapere che il valore viene caricato alla necessità all'interno del registro LDTR).
     Alla luce di quanto detto, il meccanismo per il passaggio dall'indirizzo logico (selettore:offset, registro selettore:registro indice) è illustrato dal seguente diagramma

    Passaggio da indirizzo logico a indirizzo fisico

    • Tramite il bit TI del selettore si seleziona su quale tabella dei descrittori andare a operare
    • Tramite i registri GDTR e LDTR viene selezionato l'inizio della tabella dei descrittori da utilizzare; il valore in esso contenuto viene sommato alla componente Index del selettore moltiplicata per 8 (dimensione in byte del descrittore)
    • L'indirizzo così ottenuto viene utilizzato per accedere al descrittore del segmento
    • Si controlla se l'applicazione dispone dei privilegi necessari ad accedere al segmento di memoria (confronto tra RPL e DPL). In caso contrario viene scatenata un'eccezione che blocca l'esecuzione del programma
    • Si controlla l'offset contenuto nel registro indice con il valore LIMIT del descrittore; se l'offset supera la dimensione del segmento viene generata un'eccezione che blocca l'esecuzione del programma
    • Si somma il valore BASE del descrittore con l'offset ottenendo così l'accesso alla memoria fisica
     La procedura può sembrare un po' complessa, ma l'eccellente implementazione in hardware di tale algoritmo, da parte dei progettisti Intel, fa si che possa essere eseguita in modo efficiente.

    Call-Gate

    Le "meraviglie" della modalità protetta non finiscono qui, vengono infatti introdotti i famigerati gate (letteralmente, cancello) che permettono di accedere a funzioni aventi un livello di privilegio maggiore. L'esempio più classico sono le call-gate che permettono alle applicazioni, che generalmente operano al livello di privilegio più basso, di fare chiamate a routine del sistema operativo (che operano a livello più di privilegio maggiore).


    Tramite il meccanismo dei gate tutto questo viene reso possibile. Oltre alle chiamate alla funzioni del sistema operativo (quelle che in seguito si chiameranno API), vengono introdotte le interruput-gate, per l'esecuzione delle routine di interruzione, e le task-gate che si occupano di eseguire il task-switching ossia il passaggio da un thread ad un altro per dare l'impressione di concorrenza tra più processi.

    Conclusioni

    Bene siamo quindi giunti al termine di questa articolo che, nonostante la sua inevitabile complessità, ci ha permesso di conoscere un po' meglio la seconda generazione dei processori Intel. Prima di chiudere vi invito a ragionare con me sulle seguenti considerazioni.

    L'80286 permette di accedere a 2^14 = 16382 (suddivisi equamente tra globali e locali) diversi segmenti tramite i campi Index e TI del selettore. Ogni descrittore permette l'accesso ad un segmento di 64KB (2^16). Facendo due conti si ottiene che il 286 è in grado di indirizzare virtualmente 2^14 (# segmenti) * 2^16 (ampiezza segmento) = 1GB di memoria (suddivisi equamente tra applicazioni e sistema operativo). Com'è possibile "mappare" 1GB di memoria su solo 16MB? Niente di più semplice, i segmenti non utilizzati vengono memorizzati sul disco e ricaricati quanto necessari all'elaborazione. I bit P (Presence) e A (Accessed) servono proprio a questo scopo: P dice al processore se il segmento è presente in memoria deve essere caricato sul disco, A dice al processore se il segmento è stato modificato e quindi se, in caso di necessità, deve essere scritto sul disco (se non è modificato ed è già sul disco tale operazione è inutile). Questi meccanismi, e altri che ho volutamente trascurato per non tediarvi troppo, fanno si che ogni thread abbia l'impressione di avere tutta la macchina a sua disposizione benché così non sia.

    Con il 286 inizia anche ad apparire la cache (ancora molto limita), che nel caso specifico viene utilizzata per mantenere gli indirizzi dei descrittori attivi per far si che il processore possa accedervi direttamente senza eseguire tutte le operazioni che vi ho descritto precedentemente.

    Insomma, comunque la si pensi l'80286 è stato un netto passo in avanti rispetto al suo diretto predecessore. Alcuni difetti strutturali (il "non ritorno" dalla modalità protetta, la lentezza dei gate) ne minarono il successo ma è indispensabile conoscerlo perché aprì la strada al 80386 che pochi anni dopo avrebbe contribuito a sancire il dominio dei processori Intel sulla concorrenza.

    Tutto ciò che vi ho raccontato non ha nessuna rilevanza per i giocatori o i programmatori (almeno di non lavorare a livello di kernel) ma trovo che sia interessante conoscere un po' meglio, senza scendere eccessivamente nel dettaglio, il funzionamento di quell'ammasso di silicio che permette a tutti noi di usare un personal computer. Alla prossima ....

    L'Olivetti M28, il primo personal computer della casa eporediese basato sull'80286 (1986)

    Nessun commento:

    Posta un commento