In questa serie di articoli analizzeremo il funzionamento di alcuni tipi di sistemi operativi, cercando di mostrarne le caratteristiche salienti ed i meccanismi interni.
Per iniziare, partiremo dall’analisi di un software che, pur non essendo un vero e proprio sistema operativo, presentava le funzioni basilari per leggere il contenuto della RAM, scrivere in essa e, opzionalmente, caricare e salvare intere regioni di memoria su dispositivi secondari (spesso unità a nastro magnetico o perforato).
Stiamo parlando del Machine Code Monitor.
Tali Monitor costituivano il principale software in dotazione dei primissimi sistemi di calcolo personale, parliamo di trainers e di home computers, nel periodo a cavallo tra gli anni ’70 e ’80. Leggendo e scrivendo un byte alla volta, è possibile inserire nella RAM direttamente gli opcode in linguaggio macchina, realizzando così i primi rudimentali programmi.
Generalmente bisognava scrivere prima il programma su carta, aiutandosi con i diagrammi di flusso o altro sistema schematico (pseudo codice, o altro), poi tradurre in linguaggio Assembly il programma, successivamente bisognava tradurre il listato in codice macchina e inserire i valori numerici dei singoli byte tramite il Monitor.
Versioni più evolute dei Monitor prevedevano anche un Assembler e un Disassembler, che evitavano il processo di trascrizione manuale degli opcode. Tali Monitor avanzati possiedono spesso anche delle rudimentali funzioni di debugging, tipicamente gestiscono i breakpoints e lo stepping, per cui vengono anche detti Debug Monitor.
Le funzioni principali di un tipico Debug Monitor:
Assemble: traduce una riga da Assembly a linguaggio macchina, e scrive il risultato all’indirizzo specificato.
Disassemble: legge a partire dall’indirizzo specificato effettuando la traduzione da linguaggio macchina ad Assembly.
Registers: visualizza il contenuto dei registri.
Run: carica l’indirizzo specificato sul Program Counter, di fatto serve ad eseguire un pezzo di codice che inizia all’indirizzo specificato.
Put: inserisce un byte all’indirizzo specificato.
Dump: legge un blocco di Ram e lo stampa a schermo (solitamente in esadecimale).
Load: legge un blocco di dati dalla memoria secondaria (nastro o altro) e lo carica a partire dall’indirizzo dato.
Save: prende un blocco dalla Ram e lo salva su memoria secondaria.
Per eseguire queste semplici operazioni, il Debug Monitor si serve di ulteriori funzioni non direttamente visibili all’utente, ma presenti in memoria a determinati indirizzi e che completano quello che potremmo definire come un rudimentale Sistema Operativo. Alcuni esempi di funzioni o routines, in gergo, sono:
PutChar: scrive un carattere a schermo (è la base dell’output su schermo).
PutString: si serve di PutChar per scrivere un’intera stringa di caratteri.
WriteByte: invia un byte verso una periferica di Output, usato anche per scrivere su memoria secondaria, o per settare dei registri harware.
ReadByte: legge un byte da una periferica di Input, ad esempio la tastiera.
CallMonitor: avvia il Debug Monitor.
Questo è il minimo indispensabile per poter costruire un Debug Monitor, ed è solitamente usato anche dai programmi utente per leggere e scrivere sulle periferiche, acquisire gli Input da tastiera o nastri, inviare gli Output su display o stampante. Possono inoltre essere combinate in routine di più alto livello, per eseguire compiti più sofisticati come ad esempio la gestione di un filesystem, una porta di comunicazione seriale / parallela, ecc…
In tale ambiente è chiaro che un solo programma alla volta può essere eseguito, e tale programma agisce a basso livello su ogni aspetto dell’hardware sottostante, a meno di eventuali astrazioni previste da tale rudimentale Sistema Operativo. So che in questi casi è un po’ esagerato parlare di vero e proprio Sistema Operativo, ma i concetti che introdurremo man mano ci guideranno verso la definizione di OS sempre più sofisticati, fino a giungere a quelli che utilizziamo attualmente nei nostri computer moderni.
L’astrazione è il concetto chiave che approfondiremo nel prossimo articolo, in cui presenteremo un esempio concreto di sistema operativo basato su una semplice raccolta di routines come quelle presentate: il KERNAL dei computer Commodore a 8 bit.