Linguaggio macchina

« Older   Newer »
 
  Share  
.
  1.  
    .
    Avatar

    The Girl From Mars

    Group
    Member
    Posts
    8,690

    Status
    Online
    Linguaggio macchina


    Intro

    Oggigiorno siamo abituati a usare linguaggi di programmazione cosiddetti di alto livello. Ma cosa vuol dire?
    Vuol dire che il programmatore traduce lo pseudocodice in un linguaggio di programmazione di alto livello, ovvero un linguaggio tecnico e univoco ma pur sempre vicino alla sfera umana, confidando che poi qualcun altro lo converta in un linguaggio di basso livello, ovvero un linguaggio che la macchina possa elaborare. Ma questo qualcun altro chi è? è un interprete. Eh già, un interprete, ovvero un software capace di interpretare un linguaggio vicino all’umana comprensione, seppur tecnico, e convertirlo in un linguaggio comprensibile alla macchina, ovvero basato sull’unica cosa che la macchina discerne: il sistema binario del vero-falso.
    Questo interprete è comunemente noto col nome di compilatore, i primi compilatori risalgono agli anni ’50 del secolo scorso, il primissimo fu programmato da una donna: Grace Murray Hopper, alla quale si deve l’idea stessa di compilatore, lo sviluppo del primo compilatore, l’A-0 System nel 1951, e il termine bug col quale oggigiorno sono universalmente noti i gli errori nel software che portano ad arresti anomali o a malfunzionamenti.
    Super Grace!
    Ora sorge spontanea la domanda: come si programmava prima dei compilatori? In linguaggio macchina. Ovvero ci si sobbarcava l’onere di scrivere il codice in un linguaggio comprensibile alla macchina, cioè in sequenze di vero-falso, senza praticamente alcun tipo di intermediazione. Era difficile? Sì. Però era l’unico modo e i pionieri dell’informatica facevano così.
    Ma così come? Vediamolo insieme in queste pillole di linguaggio macchina.

    Architettura

    La prima cosa da fare è conoscere il processore, ovvero l’esecutore del programma, perché non essendoci alcun intermediario tra la macchina e l'uomo sarà quest’ultimo a doversi sobbarcare l’onere (e il divertimento!) di scrivere nell’unico linguaggio che la macchina comprende: il linguaggio macchina.
    Ogni processore ha la sua specifica architettura, ma ci sono degli elementi comuni che ne rappresentano le caratteristiche di base. L’architettura è l'insieme dei criteri di progetto del dispositivo in questione, dal più piccolo circuito integrato al più complesso dei calcolatori elettronici. Nel caso delle CPU (Central processing Unit), acronimo inglese di processore centrale o unità centrale di elaborazione, l’architettura comprende i seguenti elementi principali:

    Architettura_di_un_processore_basato_su_registri_generali
    • La CU - Control Unit - è l’unità di controllo e ha il compito di coordinare tutte le azioni necessarie per l'esecuzione delle varie istruzioni.
    • L’ALU - Arithmetic-Logic Unit - è l’unità aritmetico-logica e, nell’ambito dell’informatica, è la parte di CPU preposta all’esecuzione delle operazioni aritmetico-logiche. Aritmetica e logica di basso livello meriterebbero ciascuna dei capitoli a parte…
    • I registri del processore (o interni):
      • per memorizzare i dati da elaborare e i risultati delle elaborazioni della ALU: i cosiddetti registri generali R1…Rn;
      • per memorizzare lo stato di esecuzione del programma: il PC o program counter, l’IR o instruction register, l’SR o status register;
      • per memorizzare gli indirizzi di dati da elaborare e delle istruzioni da eseguire: il MAR o memory address register e l’MDR o memory data register.
    • La memoria contiene un numero in genere molto elevato di registri, posizioni o celle nelle quali vengono memorizzati i dati e le istruzioni di un programma.
    • I bus di trasporto dati e indirizzi di memoria: il Data Bus e l’Address Bus.
    A questo punto abbiamo le basi per poter parlare dell’insieme di istruzioni del linguaggio macchina.

    Istruzioni

    L’insieme di istruzioni macchina è, in informatica ed elettronica, l’insieme delle istruzioni di basso livello che l’architettura del calcolatore offre al programmatore. Dunque l’insieme, o set, di istruzioni dipende dall’architettura e per questo viene chiamato ISA, acronimo inglese di instruction set architecture.
    L’instruction set è di fatto l’insieme di istruzioni base che il processore può eseguire e che costituiscono il suo specifico linguaggio macchina. Dunque un compilatore dovrà tradurre le istruzioni del linguaggio di alto livello in quello specifico linguaggio macchina, altrimenti quel programma non sarà eseguibile su quel processore.
    Il linguaggio macchina è scritto in codice binario. Ogni istruzione macchina è una combinazione di una o più opcode, ovvero operazioni (dall’accorpamento delle due parole inglesi operation code: codice operazione).
    L’ISA di una CPU specifica quindi l’insieme delle opcode eseguibili da quel processore centrale.

    Assembly

    Il linguaggio assembly costituisce l’ISA (Instruction Set Architecture) di un processore
    L’assembly è un linguaggio di programmazione di basso livello che usa codici mnemonici per rappresentare linguaggio macchina. Ciò a vantaggio della leggibilità del codice da parte del programmatore, che può quindi ignorare il formato binario del linguaggio macchina e scrivere il suo codice usando, al posto delle opcode, la forma mnemonica.
    La forma mnemonica permette di avere delle parole chiave per indicare le operazioni, ad esempio ADD per le somme o MOV per gli spostamenti. Permette inoltre di usare una base numerica più comoda per la scrittura di dati e indirizzi, solitamente l’esadecimale, e delle stringhe di testo come identificatori. Insomma la forma mnemonica dà al programmatore la possibilità di pensare in linguaggio macchina. Bello no?
    Sebbene l’assembly risulti decisamente più leggibile del linguaggio macchina, mantiene con questo un totale isomorfismo. Va detto esplicitamente che, per il fatto di non essere scritto in linguaggio macchina, non può essere eseguito direttamente dal processore. Per essere eseguito deve essere tradotto ed esiste un apposito compilatore che lo fa e che prende il nome di assembler, ovvero l’assemblatore.

    Esempio

    Vediamo un esempio di programma Hello world in assembly Intel x86 con sintassi Intel (sfrutta le chiamate al sistema operativo DOS).

    MODEL SMALL
    STACK 100H
    .DATA
        HW      DB      "hello, world", 13, 10, '$'
    .CODE
    .STARTUP
        MOV AX, @data
        MOV DS, AX
        MOV DX, OFFSET HW
        MOV AH, 09H
        INT 21H
        MOV AX, 4C00H
        INT 21H
    END


    E vediamo anche come la CPU esegue l’istruzione “MOV R1, D”. Ovvero lo spostamento del contenuto del registro R1 all’indirizzo di memoria D. Così da rendersi conto di quante operazioni macchina ci siano dietro ogni singola istruzione, anche la più semplice.
    I passi necessari ad eseguire questa istruzione sono:
    1. PC → MAR
    2. Inc(PC)
    3. Memory Read
    4. MDR → IR
    5. PC → MAR
    6. Inc(PC)
    7. Memory Read
    8. MDR → MAR
    9. R1 → MDR
    10. Memory Write
    Analizziamone nel dettaglio il significato. Trovandoci all'inizio della fase di fetch (cioè la lettura dell’opcode), bisogna prelevare l'istruzione dalla memoria: essa, per definizione, è contenuta all'indirizzo puntato dal registro PC.
    Il passo 1 copia il contenuto del PC nel MAR in modo da preparare la memoria ad un accesso alla giusta posizione. Il passo 2, in maniera del tutto generale, incrementa il PC in modo che esso punti alla posizione di memoria successiva, essendo questa la posizione da leggere nel seguito con la maggiore probabilità. Il passo 3 ordina alla memoria di scrivere sul Data Bus e, contemporaneamente, al MDR di leggere dal Data Bus. In seguito a questa operazione il MDR conterrà la prima parte dell'istruzione da eseguire. Essa, per essere interpretata, deve comunque essere trasferita nel IR. Questo avviene al passo 4. Una volta che l'istruzione è stata interpretata, l'unità di controllo “capisce” che l'istruzione è stata prelevata solo parzialmente e bisogna prelevarne un altro frammento. Qui la fase di fetch termina ed inizia la fase di preparazione degli operandi. Nel passo 5, di nuovo il PC viene copiato nel MAR per permettere un nuovo accesso alla memoria; immediatamente dopo, nel passo 6, viene nuovamente incrementato così da farlo puntare alla successiva istruzione, quella che verrà prelevata ed eseguita durante il ciclo successivo. Nel passo 7 la memoria viene letta e l'indirizzo D viene copiato nel MDR. Qui termina anche la fase di preparazione degli operandi ed inizia la fase di esecuzione vera e propria dell'istruzione. Dal momento che la scrittura deve avvenire proprio all'indirizzo ora contenuto nel MDR, questo viene copiato nel passo 8 sul MAR. Nel passo 9 il contenuto del registro R1 viene copiato nel MDR e infine da qui, nel passo 10, viene copiato in memoria alla giusta posizione attraverso un'operazione di scrittura. Termina così anche la fase di esecuzione. A questo punto il PC punta alla successiva istruzione da eseguire, ed una nuova fase di fetch può avere inizio.

    Conclusioni

    Questa è una piccola introduzione al meraviglioso mondo del linguaggio macchina. Spero vi sia piaciuta <3

    Edited by Weavy - 8/2/2022, 21:45
     
    Top
    .
  2.  
    .
    Avatar

    The Girl From Mars

    Group
    Member
    Posts
    8,690

    Status
    Online
    FLIP - FLOP

    Intro

    Nel post dedicato al linguaggio macchina abbiamo dato uno sguardo al cuore di ogni macchina: la CPU, dove tutti i programmi vengono eseguiti. Una delle parti di fondamentale importanza nell’architettura di ogni CPU è la memoria, cioè quella parte del dispositivo che permette di immagazzinare tutti i dati necessari all’esecuzione dei programmi. Com’è fatta la memoria? E come funziona? L’argomento è ampio e complesso, in questo post parliamo dei flip-flop, che sono circuiti elettronici usati anche come singole celle di memoria.

    Il multivibratore

    Il flip-flop appartiene alla categoria dei circuiti elettronici detti multivibratori. Il multivibratore è un circuito elettronico che ha la particolare caratteristica di avere due stati possibili, i soliti:

    • vero / falso (in termini logici), oppure

    • 1 / 0 (in termini numerici).

    A seconda che il circuito elettronico abbia o meno la capacità di mantenere il proprio stato costante (o stabile) nel tempo si identificano tre tipi di multivibratori:

    • Astabili - i due stati sono instabili: il circuito multivibratore è astabile quando nessuno dei due stati può essere mantenuto stabilmente nel tempo e il circuito commuta continuamente tra di essi. Queste continue commutazioni generano un’oscillazione, la cui forma è generalmente un’onda quadra. I multivibratori astabili sono una categoria di circuiti molto impiegata in elettronica e sono comunemente detti oscillatori.

    • Monostabili - i due stati sono uno stabile e l’altro instabile: il circuito multivibratore è monostabile quando uno dei due stati è stabile nel tempo mentre l’altro non lo è. Ciò comporta che quando il circuito è nel suo stato stabile ci resta finché una sollecitazione esterna non lo forza a commutare allo stato instabile. Quando il circuito è nello stato instabile ci resta per un tempo limitato dopodiché spontaneamente (senza altre sollecitazioni esterne) commuta nello stato stabile. I multivibratori monostabili trovano applicazione ad esempio come temporizzatori o timer.

    • Bistabili - i due stati sono stabili: il circuito multivibratore è bistabile quando entrambi gli stati sono stabili nel tempo. Questo circuito commuta di stato solo in presenza di una sollecitazione esterna. Per questa capacità di mantenere lo stato, che è un bit (0/1), stabile nel tempo, questo circuito trova utilizzo come cella di memoria statica, proprio nei registri della CPU. Questo tipo di circuiti viene comunemente chiamato flip-flop.

    Il circuito integrato noto in letteratura, su cui generazioni di studenti si esercitano, è l’NE555, che può essere opportunamente configurato per funzionare come oscillatore, come temporizzatore e come flip-flop.
    Timer-555-pinouts-construction-working-types-pinout-diagram-


    Il flip-flop JK

    x3

    Questo è il simbolo circuitale ISO del flip-flop JK con la relativa tabella di verità, ovvero la descrizione delle relazioni tra gli ingressi e le uscite. La relazione ingresso-uscita è la logica del circuito, ciò che lo contraddistingue e lo differenzia da tutti gli altri. Chiariamo innanzi tutto cosa si intende per ingresso e uscita:
    l’ingresso è un punto del circuito elettronico al quale è possibile applicare la sollecitazione esterna che ne condiziona opportunamente il funzionamento; l’uscita è un punto del circuito elettronico dal quale è possibile prelevare il dato di interesse. Nel caso del flip-flop l’uscita è il suo stato.
    Il flip-flop è un circuitosequenziale, quindi l’uscita dipende oltre che dagli ingressi, anche dallo stato. Ciò lo distingue dai circuiti combinatori, in cui l’uscita dipende solo dagli ingressi. Ma torniamo al circuito sequenziale: cosa significa che l’uscita dipende dallo stato, che abbiamo appena visto essere l’uscita stessa? Significa che la logica del circuito sequenziale, a differenza della logica combinatoria, va progettata tenendo conto del tempo, ovvero della sequenza dei valori dell’uscita nel tempo. In particolare, detta Q l’uscita, si ha che: Qn dipende dal valore degli ingressi e dal valore Qn-1, ovvero lo stato del circuito, ovvero l’uscita precedente. Riassumendo: quando l’uscita dipende anche dallo stato la tabella di verità annovera tra gli ingressi anche l’uscita precedente.
    J e K sono i due ingressi, ciascuno dei quali ha due valori possibili, quindi le combinazioni assumibili in ingresso sono quattro. Per ogni configurazione possibile degli ingressi viene dato in tabella il relativo valore che assume l’uscita Q. Q barrato (= !Q = not Q) è il valore complementare di Q e nel simbolo circuitale viene sempre indicata come una seconda uscita possibile, ma essendo dipendente dalla prima possiamo in questa sede trascurarla e considerare solo Q.
    In questa sede vediamo il JK, il tipo più comune di flip-flop in grado di immagazzinare un bit. Ci sono altri tipi di flip-flop con circuiti e relative tabelle differenti, ma in questa sede ci limitiamo a vedere il classico (e didattico) JK.
    Dopo tutte queste necessarie premesse, vediamo finalmente come funziona il flip-flop JK, talvolta abbreviato con FF-JK. Con riferimento alla tabella di verità:

    • J=0 e K=0-> BLOCK -> Qn=Qn-1: i due ingressi sono bassi, o a zero, è la configurazione di mantenimento. In questa configurazione il flip-flop permane nel suo stato precedente, quindi l’uscita in esame (Qn o semplicemente Q) ha lo stesso valore dell’uscita precedente (Qn-1). Questa caratteristica è proprio quella che gli permette di funzionare come cella di memoria statica, cioè capace di mantenere per un tempo indefinito un dato stabile.

    • J=0 e K=1 -> RESET -> Qn=0: questa configurazione di ingressi determina il reset dell’uscita, ovvero comporta che l’uscita assuma il valore zero.

    • J=1 e K=0 -> SET -> Qn=1: questa configurazione di ingressi determina il set dell’uscita, cioè impone che l’uscita vada a uno.

    • J=1 e K=1 -> TOGGLE -> Qn=!Qn-1: quando i due ingressi sono entrambi alti, o a uno, il flip-flop è in configurazione di commutazione. In questo stato il flip-flop commuta, quindi se lo stato precedente era basso Q andrà a uno, viceversa se lo stato precedente era alto Q andrà a zero.

    I due stati interessanti sono quelli in cui è presente la sequenzialità: il block e il toggle. Per approfondire queste due modalità di funzionamento bisogna capire bene che cos’è il clock e come funziona.

    Clock e temporizzazioni

    Da quanto detto fin qui è emersa tra le righe la necessità di temporizzare il funzionamento dei circuiti sequenziali. Esplicitiamo ora questo concetto.
    Considerare, come si è visto, le uscite del flip-flop come una sequenza discreta di stati{Qn, Qn-1, Qn-2, …}, è possibile solo se si ha un riferimento temporale discreto che scandisca in maniera univoca i passi {n, n-1, n-2, …}. Per generare questo riferimento si fa uso di un segnale periodico, generalmente un’onda quadra, al quale si dà il nome di clock.

    x2

    L’onda quadra (Ck in figura) è caratterizzata da:

    • due fronti di commutazione: i momenti di transizione alto-basso e basso-alto,

    • due livelli stabili corrispondenti ai due stati logici alto e basso o uno e zero.

    Quelli che in figura hanno la freccia verso l’alto sono i fronti di salita, gli istanti che scandiscono i passi della sequenza del flip-flop. In quei particolari istanti viene recepito il valore degli ingressi e prodotta l’uscita, che poi resta stabile fino al fronte successivo.
    Ora è possibile comprendere che:
    per JK=00 (block) il FF si comporta come cella di memoria, mantenendo il bit di stato stabile,
    per JK=11 (toggle) il FF si comporta come oscillatore, producendo in uscita un’onda quadra con periodo doppio rispetto al clock.
    Fintanto che J e K restano alti il flip-flop continua a commutare oscillando a frequenza dimezzata. Questo è l’aspetto forse più interessante, almeno didatticamente parlando, che permette di usare la fantasia per combinare linee di flip flop opportunamente connessi tra loro al fine di ottenere in uscita l’onda desiderata. Nella figura che segue, a titolo di esempio, la configurazione più classica di tutte: il divisore di frequenza, configurazione che generazioni di studenti hanno implementato col suddetto NE555!

    divisore_freq


    Conclusioni

    Questo che abbiamo visto è uno dei tantissimi circuiti elettronici che trovano largo uso nelle reti logiche largamente utilizzate al giorno d’oggi. Come ballerini di fila, che muovendosi armoniosamente a tempo tra loro danno origine allo spettacolo teatrale, così questi piccoli circuiti logici, commutando diligentemente al ritmo del clock, danno vita alla miriade di dispositivi elettronici che ormai sono entrati nella quotidianità di tutti.

    Edited by Weavy - 14/2/2022, 18:06
     
    Top
    .
1 replies since 11/12/2021, 19:07   83 views
  Share  
.