MASSIMO UBERTINI KERNEL WWW.UBERTINI.IT MODULO 1 KERNEL PER LA MULTIPROGRAMMAZIONE Introduzione Soluzione Implementazione Laboratorio Quarta due um 1 di 42 INTRODUZIONE Finalità Per realizzare su un PC le funzioni di multiprogrammazione si possono seguire tre metodi. 1. Utilizzare un linguaggio che offra le funzioni necessarie alla protezione delle risorse, alla sincronizzazione e allo scambio di messaggi tra processi: IPC (Inter Process Communication), per esempio Ada. 2. Utilizzare le funzioni di multiprogrammazione messe a disposizione dal SO, iRMX III è un SO real-time di INTEL con multitask di tipo preemptive su architettura 80386 con 4 MB di RAM, MS-DOS è caricato nella memoria convenzionale e incapsulato come processo a bassa priorità, mentre RMX è caricato in XMS; RMOS for Windows, della Siemens, è un SO multitasking real time che gestisce MS-DOS e Windows come task senza limitazioni, le applicazioni Windows comunicano con RMOS mediante DDE (Data Dynamic Exchange); le principali aziende che si occupano di RTOS (Real Time Operating System) sono: Microsoft e Microware. 3. Definire e realizzare le proprie funzioni di supporto della multiprogrammazione. Se praticabile, il primo metodo è certamente il più conveniente. In alternativa, il secondo metodo presenta un certo interesse, specie se si vuole fare uso di un linguaggio ad alto livello. In questo caso è sufficiente apprendere i richiami verso il SO per realizzare le funzioni di multiprogrammazione. Se le prime due strade non sono percorribili, è necessario realizzare in proprio le funzioni di gestione e supporto della multiprogrammazione. Quest’impresa richiede esperienza di programmazione assembly e una buona conoscenza di MS-DOS. Dato che questo SO di per sé non sostiene la multiprogrammazione, è necessario costruire un nucleo di controllo, kernel, al quale spetta sia la gestione dell’esecuzione dei processi, scheduler, sia l’implementazione delle funzioni di coordinamento. KERNEL Permette l’esecuzione contemporanea di più processi, anche con attività rigorosamente suddivise, i processi restano in un certo grado dipendenti l’uno dall’altro. In principio l’accesso a ognuna delle risorse fisiche è limitato a un solo processo alla volta e idealmente ad un solo processo in generale; se processi diversi hanno necessità di accedere alla stessa risorsa, essi possono farlo indirizzando le loro richieste a un processo comune di gestione. SCHEDULER Assegna un processo alla CPU, la politica di gestione è la seguente. Turn around time o throughput. Ha due compiti. 1. Come il processo restituisce il controllo alla CPU. 1.1. Preemptive: il processo è interrotto da un processo a più alta priorità. 1.2. Non preemptive: il processo che ottiene la CPU la mantiene fino al termine del suo quanto di tempo. 2. Quale processo mandare in esecuzione. 2.1. Batch: gestione a priorità, tre importanti strategie sono: FIFO, lavoro più corto per primo, preveggente, dipende dall’esperienza. 2.2. Multitasking: round-robin o code multiple. Laboratorio Quarta due um 2 di 42 PROCESSO Esecuzione di un’immagine. IMMAGINE Ambiente di esecuzione formato da: istruzioni, dati, registri, file aperti e directory corrente. Un nucleo per la multiprogrammazione può essere realizzato secondo due filosofie differenti, orientate a due diversi metodi di programmazione. Nel primo caso i processi sono rigorosamente separati, ogni processo ignora l’esistenza degli altri. In particolare, più processi possono cercare di accedere contemporaneamente alle stesse risorse H/W. Il SO e il nucleo di gestione devono quindi controllare e ordinare le richieste provenienti dai singoli processi in modo che non risultino conflitti e deadlock. È tipico dei centri di calcolo multiutente. DEADLOCK Un insieme di processi tiene delle risorse, di cui non è possibile forzare il rilascio, che sono attese da altri processi per continuare l’esecuzione. Il verificarsi di queste condizioni rende possibile il deadlock. Mutua esclusione delle risorse. Attesa durante l’assegnazione di una risorsa. Scheduler non preemptive. Attesa circolare. Nel secondo caso le applicazioni sono state esplicitamente sviluppate per il sistema di multiprogrammazione: ogni processo è dedicato all’esecuzione di attività specifiche e coordina con gli altri l’accesso alle risorse comuni. Qui non è necessario che il kernel segua fino in dettaglio le operazioni eseguite dai singoli processi. È tipico dei sistemi real-time, dove le applicazioni sono sviluppate per agire in maniera coordinata, per esempio la gestione delle eccezioni: non si può fermare un’applicazione per division by zero in un sistema real time. RTOS “Le informazioni devono essere fornite in un tempo tale da essere usate per influenzare, interrompere o far proseguire il processo che le ha generate”. Esempio, controllo di satelliti e processi, transazioni finanziarie. Data processing: Office automation: Teleprocessing: Controllo di processo: 15..20 msec. 500 msec. minuti. 1 msec. real time duro! I sistemi general-purpose richiedono 5000 µsec per un interrupt, i sistemi real time 15 µsec. Il kernel è stato sviluppato tenendo anche presente il secondo caso, in quanto esso è il più comune per i sistemi di controllo digitali e perché così è più facile capire il funzionamento dei metodi di cooperazione tra processi. Le finalità principali del nucleo di gestione della multiprogrammazione sono così riassumibili. Esecuzione parallela di processi diversi, dividendo tra loro il tempo di CPU. Laboratorio Quarta due um 3 di 42 Possibilità di sincronizzazione tra processi. Disponibilità di un metodo di temporizzazione. Possibilità di IPC. È possibile definire le specifiche funzionali del nucleo di controllo. 1. Kernel e routine di supporto scritte in assembly. 2. Multitasking: esecuzione parallela di un massimo di 8 processi diversi. 3. Dev’essere possibile il caricamento di applicazioni scritte in un linguaggio qualsiasi che, una volta compilate e linkate, sono eseguibili in MS-DOS; il nome delle applicazioni da caricare va passato come parametro al richiamo del nucleo di programmazione. 4. Semafori: dev’essere disponibile un semaforo con relative routine di accesso: Signal e Wait_sem. 5. Funzione di attesa per un tempo variabile, passato come parametro in secondi. 6. Per la comunicazione tra processi devono essere disponibili almeno 4 mailbox di dimensioni minime 256 byte, con relative operazioni di lettura e scrittura. 7. L’accesso allo schermo e alla tastiera dev’essere coordinato tra i processi e non portare a situazioni di deadlock. 8. Lo stato del sistema dev’essere presentato continuamente su schermo e dev’essere possibile seguire lo stato dell’esecuzione dei processi. Laboratorio Quarta due um 4 di 42 SOLUZIONE INTRODUZIONE Il nucleo di controllo per la multiprogrammazione e le funzioni d’interazione tra processi sono realizzati nell’applicazione MTRUN, che è caricata ed eseguita in MS-DOS. Quando MTRUN è richiamata occorre indicare nella linea di comando i nomi delle applicazioni da caricare con relativa estensione, per default EXE. MTRUN provvede anche alla gestione di funzioni di base di sistema: scambio di processi, gestione di un orologio interno, accettazione dell’ingresso da tastiera; contiene un semaforo, le mailbox e una coda di attesa processi per intervalli di tempo. MTRUN basa il suo funzionamento in parte su MS-DOS, in parte ne sostituisce le funzioni con altre proprie; non controlla se i processi richiamano direttamente funzioni MS-DOS, al fine di evitare problemi è però consigliabile che i processi in esecuzione sotto MTRUN ne sfruttino le funzioni ogni volta che sia possibile. TICK DI SISTEMA La chiave per la realizzazione delle funzioni di multiprogrammazione è l’intercettazione dei segnali d’interruzione inviati dal temporizzatore di sistema. Nel PC sono generate interruzioni alla frequenza di 18.2 volte al secondo. Queste interruzioni servono per l’aggiornamento dell’orologio interno e per la prosecuzione della routine di stampa file. Il sistema passa quindi il controllo all’applicazione il cui indirizzo si trova nel vettore delle interruzioni con il riferimento INT 1CH. Normalmente l’indirizzo contenuto punta a un’istruzione di ritorno IRET. Se però il riferimento 1CH corrisponde a un’applicazione esistente, questa applicazione è passata in esecuzione al giungere di ogni tick di sistema. MTRUN fa uso di quest’interruzione per salvare sullo stack lo stato del processo corrente, svolgere alcune operazioni di aggiornamento dati interni e decidere se il processo corrente va o non fermato e a quale processo spetta quindi di proseguire. MTRUN non ritorna il controllo a MS-DOS al termine delle sue operazioni. Per questo deve eliminare dallo stack in uso i puntatori alla routine MS-DOS che ha chiamato INT 1CH e completarne le funzioni, inviando un segnale per ripristinare le funzioni dell’integrato PIC 8259A. La scansione del tempo nel PC prevede due attività. TCS (TICK COUNTER SERVICE) Il TCS è legato al timer di sistema e indica l’interrupt che è lanciato alla CPU ogni 54.9 msec utilizzato anche per aggiornare data e ora. Al reset il BIOS inizializza il TCS attingendo dal RTC le informazioni necessarie. Da queste ricava il valore da inserire in quattro locazioni di memoria, definite TC (Tick Counter), allocate agli indirizzi 40:6CH..40:6FH. In questi 32 bit è registrato il numero d’interrupt arrivati ogni 54.9 msec. Quando questo contatore raggiunge il valore 1573040, sono passate 24 ore per cui il BIOS lo resetta e registra l’evento attivando un particolare flag alla locazione 40:70H. Un’interruzione S/W, vettorizzata 1CH, corrispondente alle locazioni 00:70H..00:73H, è generata ogni 54.9 msec. La routine di risposta di default è IRET. L’utente può modificare questa situazione forzando l’indirizzo di una propria routine che è eseguita 18.2065 tick volte al secondo facendo però attenzione a ripristinare il contesto in uscita, per non alterare il funzionamento del sistema. Laboratorio Quarta due um 5 di 42 Riguardo all’interrupt 1CH bisogna tener presente che ogni 54.9 msec. (18.2065 volte al secondo, 1 sec / 18.2065 = 0.0549) il canale 0 del PIT 8253 (Programmable Interval Timer) genera un INT 08H sulla linea IRQ0 del PIC 8259 (Programmable Interrupt Controller), associata alle locazioni 20H..23H nella tabella degli interrupt, che, a sua volta, lancia l’INT 1CH. Per condizionare le funzioni del TCS il BIOS prevede l’INT 1AH. 1. INT 1AH con AH = 00: lettura del contatore TC, trasferisce in DX e CX, rispettivamente, la parte bassa e alta del TC; il valore del flag indicante le 24 ore è copiato in AL. Dopo la lettura il flag è azzerato. 2. INT 1AH con AH = 01: inizializzazione TC, consente di modificare il TC e di azzerare il flag delle 24 ore. RTC (REAL TIME CLOCK) Questa funzione, gestita dall’integrato MC146818 insieme con quella di memorizzazione della configurazione H/W del sistema, prevede tre specifiche attività. 1. Aggiornamento e mantenimento di data e ora. 2. Controllo di data e ora per la generazione di allarmi, al tempo stabilito dall’operatore, mediante un interrupt vettorizzato con il puntatore 4AH locazione 128H. 3. Generazione di un interrupt H/W su IRQ8 del PIC slave, vettorizzato con il puntatore 70H, ogni 976 µsec. La struttura interna di quest’integrato può essere schematizzata come un blocco di 64 locazioni di memoria. Le prime 14 sono destinate alla scansione della data e dell’ora con i rispettivi allarmi, le altre 50 conservano informazioni relative alla configurazione H/W del sistema. Locazione 00H 01H 02H 03H 04H 05H 06H 07H 08H 09H 0AH 0BH 0CH 0DH 0EH 0FH 10H 11H 12H 13H 14H 15H..16H 17H..18H 19H..2CH 2DH 2EH..2FH 30H..31H Funzione Riservata al conteggio dei secondi. Contiene il valore di riferimento dei secondi per la generazione degli allarmi. Riservata al conteggio dei minuti. Contiene il valore di riferimento dei minuti per la generazione degli allarmi. Riservata al conteggio delle ore. Contiene il valore di riferimento delle ore per la generazione degli allarmi. Contiene il giorno della settimana. Contiene il giorno del mese. Contiene il mese. Contiene l’anno. Registro di stato A. Registro di stato B: LSB=1 abilita l’ora legale. Registro di stato C. Registro di stato D. Registro di diagnostica. Codice di reset: 04H caricamento del DOS. Tipo di drive per floppy. Riservato. Tipo di drive per hard disk. Riservato. H/W installato. Dimensione memoria della system board. Memoria estesa installata. Riservato. Riservata all’utente per la definizione di particolari configurazioni di sistema. Checksum applicato alle locazioni 10H e 20H. Definiscono la quantità di memoria disponibile oltre il primo mega. Laboratorio Quarta due um 6 di 42 32H 33H 34H..3FH Contiene l’indicazione del secolo in BCD. Configurazione di sistema. Riservato. L’MC146818 è mappato in I/O agli indirizzi 70H e 71H. Per accedere alle singole locazioni. MOV AL,12H OUT 70H,AL IN AL,71H ;seleziona la locazione ;lettura della locazione. Sei versioni dell’INT 1AH interagiscono con le funzioni del RTC. 1. INT 1AH con AH = 02: lettura del tempo corrente, in CX (ore e minuti) e in DX (secondi e ora solare) i valori sono in BCD. Il tempo così letto può essere diverso da quello del timer di sistema (locazioni 40:6CH..40:6FH) che, solo all’inizio, è caricato con il valore del RTC. 2. INT 1AH con AH = 03: modifica del tempo corrente, questa operazione non influenza l’indicazione del timer di sistema (TC). 3. INT 1AH con AH = 04: legge la data, in CX (secolo e anno) e in DX (mese e giorno) i valori sono in BCD. 4. INT 1AH con AH = 05: modifica la data nel formato secolo, anno, mese, giorno. 5. INT 1AH con AH = 06: definisce i valori di riferimento per la generazione degli allarmi, in CX (ore e minuti) e in DX (secondi) i valori sono in BCD. Quando il tempo corrente è uguale a quello impostato è generato un interrupt 4AH corrispondente alle locazioni 128..12BH. Se non è predisposto altro riferimento, l’interrupt scatta ogni 24 ore per aggiornare la data e resettare i contatori. 6. INT 1AH con AH = 07: resetta la condizione di allarme bloccando l’INT 4AH. PROCESSI MTRUN legge dalla linea di comando i nomi delle applicazioni da caricare. Ogni processo è messo in una partizione dedicata della memoria, dove gli sono allocati uno spazio dati, uno di codice, uno di stack e un’area libera heap. Le dimensioni della memoria da allocare sono calcolate dalle indicazioni contenute nel file header dell’applicazione. MTRUN divide l’attività della CPU tra i processi secondo il principio round-robin. Quando un processo è interrotto, le informazioni relative al suo contesto momentaneo, copia di tutti i registri, sono messe sullo stack. Il nucleo di gestione dello scambio tra processi gestisce solo i puntatori ai diversi stack. Le strutture di dati per la gestione dei processi sono: READY il buffer circolare dei processi pronti, 8 byte, con i puntatori al processo corrente CURRENT e al primo spazio libero dopo l’ultimo dei processi in attesa, READY_NEXT. Anche se sarebbe sufficiente definire questi puntatori come byte prendono i valori da 0 a 7, per comodità in alcune operazioni di puntamento essi sono definiti come word. Il numero di processi attualmente contenuti in READY è registrato in READY_CNT, un byte e il numero totale di processi caricati in PROC_TOTAL, un byte. Ogni processo è identificato internamente da un numero (0..7) assegnato al momento del caricamento. Il numero del processo è l’indice di accesso alle strutture dati SAVE_STACK, 16 word, dove sono registrati i puntatori SS:SP, 2 word, agli stack dei processi e PROC_NAME, 64 byte, dove sono depositati i nomi dei processi caricati, un nome 8 byte. Il numero del processo è utilizzato anche per la sua identificazione nel buffer di esecuzione e nelle code di attesa. Lo scambio dei processi, context switch, avviene nel modo seguente. Laboratorio Quarta due um 7 di 42 A ogni processo è assegnato un periodo di tempo, time slice, di esecuzione. Questo periodo è dell’ordine di grandezza di 5 tick, corrispondenti a circa 0.27 sec. Il numero di tick che trascorrono per ogni processo è conteggiato nella variabile TICK. Al giungere di ogni interruzione del temporizzatore di sistema, è eseguito il codice seguente. TICK++; if (TICK==TICK_MAX) { // il processo corrente dev’essere interrotto // lo stato di tutti i registri è salvato sul suo stack // SS:SP sono copiati in SAVE_STACK // MTRUN fa uso del proprio stack prima di passare il controllo a un altro processo TICK=0; // per poter servire da riferimento a un nuovo processo } Per CPU non progettate per il multitasking, ad esempio 8086. PUSH AX PUSH BX PUSH CX PUSH DX Per CPU progettate per il multitasking, ad esempio 80286. PUSHA POPA ; istruzioni compatte per il salvataggio e il ripristino Il numero del processo interrotto è scritto in fondo al buffer READY, nella locazione alla quale punta READY_NEXT. Se però il processo è in attesa di una segnalazione del semaforo o per un intervallo di tempo, esso è inserito nella coda relativa di attesa. Il nuovo processo da passare in esecuzione è quello contenuto nel buffer READY, nella locazione alla quale punta CURRENT++. Può accadere che tutti i processi si trovino al momento nelle code di attesa. if (READY_CNT==0) BUSY_LOOP$; Nel qual caso l’esecuzione passa al ciclo di attesa BUSY_LOOP$. La funzione di questo ciclo è quella di non fare assolutamente nulla e attendere l’arrivo del successivo tick di sistema. L’attesa è in questo caso giustificata in quanto la CPU non ha altri processi ai quali dedicarsi, in pratica è un processo, nome proprio NULL, con priorità minima, da eseguirsi solo quando nessun altro processo è pronto per l’esecuzione. L’esecuzione di BUSY_LOOP$ è segnalata per mezzo dei flag seguenti. (BUSY_STATE==1) && ((NULL_STATE==1) A ogni nuovo tick è controllato lo stato di questi flag se ci si trova in BUSY_LOOP$, lo stato corrente dei registri non è salvato. SEMAFORI È un flag a guardia della risorsa: H/W o S/W. In un semaforo sono definite le operazioni di segnalazione SIGNAL e di attesa Laboratorio Quarta due um 8 di 42 WAIT_SEM. Le strutture per l’implementazione del semaforo comprendono la variabile SEMAPHORE, 1 byte, il buffer circolare per la coda di attesa SEM_WAIT, 8 byte, con i relativi puntatori SEM_FIRST, una word, punta alla cella contenente il numero del primo processo in attesa e SEM_NEXT, una word, punta al primo spazio libero per l’inserimento di un nuovo processo. La variabile SEM_CNT, un byte, contiene il numero dei processi in coda di attesa per il semaforo. Quando un processo chiama WAIT_SEM, è innanzitutto controllato il valore del semaforo. if (SEMAPHORE>0) { SEMAPHORE--; // il processo prosegue } if (SEMAPHORE==0) { // il processo attende in BUSY_LOOP$ fino al successivo tick // è tolto dalla coda dei processi pronti READY // è inserito in quella di attesa del semaforo SEM_WAIT SEM_CNT++; SEM_NEXT++; } La segnalazione allo scheduler che il processo corrente va inserito in coda di attesa semaforo è effettuata con la variabile DISPOSAL_MODE = 1. Al richiamo della funzione SIGNAL è controllato il numero dei processi in attesa contenuto in SEM_CNT. if (SEM_CNT==0) { SEMAPHORE++; // l’esecuzione procede; } else { // ci sono processi in attesa, SEMAPHORE non è modificato // il numero relativo al primo dei processi è letto dalla coda SEM_WAIT // ed è messo in quella dei processi pronti READY SEM_CNT--; SEM_FIRST++; } La funzione SIGNAL è chiamata con INT 62H, AH = 01H e controlla il numero dei processi in attesa. La funzione WAIT_SEM è chiamata con INT 62H, AH = 02H e controlla il valore del semaforo. ATTESA DI UN INTERVALLO DI TEMPO La funzione WAIT_TIME è utilizzata per fare attendere un processo per un determinato periodo di tempo. Una volta trascorso questo tempo, il processo può continuare l’esecuzione. I numeri dei processi sono ordinati in una coda, TIME_WAIT, 8 byte. Ogni processo è registrato con il tempo di attesa in tick; a ogni interruzione proveniente dal temporizzatore di sistema il valore del tempo di attesa è aggiornato. I processi in attesa sono ordinati in modo che il primo processo sia quello con il tempo di attesa inferiore, il secondo quello con il tempo di attesa immediatamente superiore e così via. Laboratorio Quarta due um 9 di 42 I tempi di attesa registrati in TIME_WAIT non sono assoluti, ma relativi al tempo del processo precedente. Con quest’organizzazione è sufficiente aggiornare solo il primo dei tempi di attesa. Alla chiamata WAIT_TIME il processo è fermato e il tempo di attesa richiesto confrontato con quello dei processi già presenti in coda, in modo da inserire il processo nel punto corretto dipendente dall’ordine di attesa. La variabile TIME_CNT, un byte, che contiene il numero totale dei processi in attesa, è incrementata di un’unità. La funzione WAIT_TIME è chiamata con INT 62H, AH = 03H. Il tempo richiesto di attesa, in tick, è passato in DX e poi copiato nella variabile TIME_INTERV. La segnalazione allo scheduler che il processo corrente va inserito in coda di attesa per un intervallo di tempo, avviene con la variabile DISPOSAL_MODE = 3. MAILBOX Le mailbox sono realizzate con strutture di dati localizzate in successione in MBX_SPACE, un array di puntatori all’inizio di ogni mailbox, MBX_PTR, una struttura che indica il numero dei messaggi contenuti nelle rispettive mailbox MBX_MSGS, una che tiene conto dello spazio libero MBX_SPACE, una che punta al primo messaggio MBX_FIRST e una che punta al primo spazio libero al termine dell’ultimo messaggio MBX_NEXT. Il numero e la lunghezza delle mailbox sono definibili con due parametri indicati all’inizio di MTRUN, MBX_NUM, un byte, massimo quattro da zero a MBX_NUM-1 e MBX_LEN, un byte. Tutte le strutture di dati sono array e l’accesso alla variabile d’interesse è dato facendo uso del numero della mailbox da zero a MBX_NUM-1 come indice. Un messaggio è registrato in una mailbox come stringa di caratteri di lunghezza variabile. Il primo byte indica la lunghezza del messaggio da 0 a 255 byte, i caratteri successivi contengono il messaggio vero e proprio. La registrazione di un messaggio di lunghezza n richiede quindi n+1 byte. Le funzioni definite sulla mailbox sono la scrittura, PUT_MAILBOX che accetta due parametri il numero della mailbox [0..MBX_NUM - 1] e un puntatore al messaggio nella forma SEG:OFF, ritorna una word che assume i valori 0 = funzione OK, 1 = spazio insufficiente, 4 = numero mailbox errato; la lettura, GET_MAILBOX che accetta come parametri il numero della mailbox e il puntatore alla stringa nella quale va trasferito il messaggio, ritorna una word che assume i valori 0 = funzione OK, 2 = non ci sono messaggi, alla stringa di uscita è passato in questo caso un parametro indicante lunghezza zero, 4 = numero mailbox errato. La funzione PUT_MAILBOX è chiamata con INT 62H, AH = 05H. Il numero della mailbox è contenuto in AL; ES:SI deve puntare alla stringa da copiare nella mailbox, il primo carattere indica la lunghezza del messaggio. GET_MAILBOX è chiamata con INT 62H, AH = 06H. AL contiene il numero della mailbox ed ES:DI punta alla stringa di destinazione del messaggio. GESTIONE TASTIERA Questa routine è automaticamente chiamata al premere di un qualsiasi tasto. A ogni tick di sistema è, infatti, controllato, tramite BIOS, se sono presenti nuovi caratteri nel buffer di tastiera. In tal caso il processo corrente è fermato e l’esecuzione procede con la routine READ_KEYBOARD$. Fa uso di moduli BIOS per leggere i caratteri in ingresso; sono accettati tutti i caratteri ASCII compresi tra 32 e 127. Questi caratteri sono inizialmente scritti nel buffer KBD_BUFFER il cui contenuto è Laboratorio Quarta due um 10 di 42 continuamente mostrato nella riga inferiore dello schermo. Il carattere <BACKSPACE> è utilizzato per cancellare il carattere all’immediata sinistra del cursore e <INVIO> per validare un ingresso. Dopo aver premuto <INVIO>, eventuali nuovi caratteri in ingresso sono direttamente scartati fino a che un processo legge il contenuto del buffer con il richiamo READ_STRING. L’uscita dalla routine di gestione tastiera avviene dopo che è trascorso un tempo determinato di attesa dall’ultimo ingresso di carattere oppure immediatamente in seguito alla battitura di <INVIO>. Il timeout per l’uscita da READ_KEYBOARD$, in tick, è definito nella costante KBD_TIMEOUT. if (KBD_TICK==KBD_TIMEOUT) exit; I caratteri contenuti nel buffer sono letti dall’applicazione in un’operazione unica per mezzo della routine READ_STRING. Questa routine copia i caratteri inseriti fino alla battitura di <INVIO> in una stringa il cui indirizzo è passato come parametro. In pratica, la routine d’ingresso da tastiera funziona come un’applicazione indipendente con la massima priorità, chiamata automaticamente premendo un tasto qualsiasi. Le strutture di dati necessarie per la gestione della tastiera sono il buffer d’ingresso KBD_BUFFER, 74 byte, il puntatore alla nuova posizione libera, KBD_PTR, una word, il flag KBD_LOCK che indica se l’ingresso di dati è libero. if (KBD_LOCK==0) // posso inserire caratteri; else bloccato dopo un <Return> La richiesta di passaggio del controllo alla routine di gestione tastiera READ_KEYBOARD$ è fatta premendo un tasto qualsiasi, in seguito al quale il flag KBD_REQUEST prende il valore uno. if (KBD_REQUEST==1) READ_KEYBOARD$; if (KBD_INPUT==1) // il programma corrente è READ_KEYBOARD$ e non va interrotto KBD_EXIT indica quando lo scheduler può passare il controllo dalla routine tastiera a un altro processo e KBD_TICK, un byte, serve al conteggio del timeout per l’interruzione della routine tastiera. USCITA A VIDEO L’area video è una zona della RAM, dove ogni carattere è registrato in 2 byte: uno per il codice ASCII e uno per definire l’attributo. La presentazione di stringhe su schermo avviene semplicemente copiando direttamente i relativi caratteri nell’area della memoria video. Per l’indirizzamento in area video si fa uso di una struttura di overlay, definita all’inizio dell’applicazione, nella quale sono definiti i puntatori all’area video. STATO DEL SISTEMA (PSTAT IN UNIX) Per ciascun tick è presentata sullo schermo una stringa di stato del sistema, STATUS_STR, contenente l’ora attuale, il nome del processo in esecuzione, il valore di SEMAPHORE e il numero dei processi in attesa del trascorrimento di un intervallo di tempo, contenuto in WAIT_CNT. Questa stringa appare nella metà destra della riga superiore dello schermo. Laboratorio Quarta due um 11 di 42 Il nome dei processi è quello indicato al caricamento, senza estensione. Il processo nullo BUSY_LOOP$ è riportato come “NULL” e la routine di gestione tastiera come “KEYBOARD”. COMPATIBILITÀ CON MS-DOS Il SO MS-DOS è stato progettato per il monotasking, questo significa che non è possibile contare sul fatto che i suoi moduli siano rientranti, è usabile serialmente. Codice rientrante Il codice è chiamato contemporaneamente da più processi, significa condivisione di codice, in altre parole risparmio di memoria. Dato che i processi caricati fanno direttamente uso di MS-DOS, occorre stabilire un procedimento che impedisca la chiamata contemporanea a MS-DOS da parte di più processi. La soluzione qui adottata è quella di una variabile, DOS_LEVEL, che è incrementata a ogni chiamata MS-DOS. if (DOS_LEVEL !=0) // disabilito il context switch In modo da evitare di passare il controllo a un processo diverso che a sua volta potrebbe richiamare funzioni MS-DOS. Per intercettare le chiamate al SO, l’INT 21H originale è ridefinito come INT 61H e i vettori originali d’INT 21H sono indirizzati al modulo di MTRUN INT21_HANDLER. Una chiamata al sistema originale è quindi presa prima in carica da MTRUN, che incrementa il valore di DOS_LEVEL, e quindi passata al SO con il nuovo INT 61H. Al ritorno da MS-DOS e prima di saltare all’applicazione originale, MTRUN diminuisce il valore di DOS_LEVEL. La variabile DOS_LEVEL è usata anche per la protezione delle funzioni di MTRUN. Un processo non può quindi essere fermato durante l’accesso a qualcuna di tali funzioni. MTRUN non ritorna il controllo a MS-DOS al termine delle sue operazioni, deve eliminare dallo stack i puntatori alla routine MS-DOS che ha chiamato INT 1CH. IP CS FLAGS DX AX DS IP CS FLAGS DX AX DS IP CS FLAGS temporizzatore di sistema temporizzatore di sistema temporizzatore di sistema BUSY_LOOP$ (se è stato chiamato) BUSY_LOOP$ (se è stato chiamato) BUSY_LOOP$ (se è stato chiamato) BUSY_LOOP$ (se è stato chiamato) BUSY_LOOP$ (se è stato chiamato) BUSY_LOOP$ (se è stato chiamato) processo originale processo originale processo originale processo originale processo originale processo originale E completare le funzioni del temporizzatore di sistema inviando un segnale per ripristinare il PIC 8259A. Laboratorio Quarta due um 12 di 42 INIZIALIZZAZIONE Una parte di MTRUN è usata solo al momento del caricamento iniziale delle applicazioni. Essa serve all’interpretazione della riga di comando estraendo il nome dei file in essa contenuti. Se un’estensione non è esplicitamente indicata, è presa quella di default EXE. In caso di errore, se ad esempio il file da caricare non esiste, MTRUN si arresta e il controllo torna al SO. Una volta caricate tutte le applicazioni, sono definiti i puntatori alle interruzioni INT 1CH, indirizzato al modulo INT1C_HANDLER, INT 21H è ridefinito come INT 61H le chiamate alle funzioni MS-DOS sono intercettate dal modulo INT21_HANDLER, INT 62H indirizzato a INT62_HANDLER. In seguito sono definiti i puntatori alle mailbox MBX_PTR in funzione del numero MBX_NUM e lunghezza MBX_LEN delle mailbox in modo da evitare di ricalcolarli in occasione di ogni chiamata di lettura/scrittura. Quale ultima operazione sono assegnati i valori iniziali ai puntatori alle code dei processi READY, SEM_WAIT e TIME_WAIT. Il controllo dell’esecuzione non è passato direttamente al primo dei processi in READY, ma è eseguito un salto a BUSY_LOOP$. Al primo tick di sistema, BUSY_LOOP$ è interrotto e il controllo è assegnato dallo scheduler al primo processo in attesa. Laboratorio Quarta due um 13 di 42 IMPLEMENTAZIONE INTRODUZIONE L’applicazione si divide in sette parti principali. Area dati Variabili per la gestione dei processi. Semaforo. Code di attesa intervallo di tempo. Mailbox. Ingresso da tastiera e uscita su schermo. Altre variabili. Overlay video. Stack 1 K word. Gestione INT 1CH Operazioni preliminari, salvataggio contesto. Aggiornamento orologio. Scrittura su schermo dei parametri di sistema. Aggiornamento coda di attesa di tempo. Context switch. Processo di attesa BUSY_LOOP$ Processo d’input tastiera READ_KEYBOARD$ Gestione INT 62H (1) semaforo SIGNAL. (2) semaforo WAIT_SEM. (3) attesa WAIT_TIME. (4) lettura ora GET_TIME. (5) scrittura in mailbox PUT_MAILBOX. (6) lettura da mailbox GET_MAILBOX. (7) ingresso da tastiera READ_STRING. INT 61H, chiamata DOS (INT originale 21H) Funzioni iniziali Interpretazione linea di comando. Caricamento applicazioni. Definizione dei nuovi puntatori. Salto a BUSY_LOOP$. La prima parte è l’area dati contenente le variabili usate sia localmente sia da applicazioni esterne, tutte le strutture di gestione dei processi, lo stack e l’overlay per scrittura diretta in RAM video. La seconda parte è quella richiamata a ogni interruzione del temporizzatore di sistema. A ogni tick occorre provvedere a salvare i registri dei processi su stack, ad aggiornare l’orologio interno, ogni CLOCK_MAX tick, a scrivere sullo schermo i parametri correnti di sistema e il contenuto del buffer d’input, ad aggiornare lo stato dei processi in attesa di tempo e infine scambiare i puntatori agli stack se spetta a un nuovo processo continuare l’esecuzione. La terza parte contiene il processo di attesa BUSY_LOOP$. La quarta parte contiene il processo di lettura da tastiera READ_KEYBOARD$. Nella quinta parte sono realizzate le funzioni run time chiamate tramite l’INT 62H. La sesta parte contiene il codice al quale punta l’INT 21H in modo da filtrare, tramite la variabile DOS_LEVEL, le chiamate dirette alle routine MS-DOS. L’ultima parte è chiamata soltanto in fase iniziale dell’applicazione, provvede a interpretare la linea di comando, caricare le applicazioni, definire i nuovi puntatori per le interruzioni Laboratorio Quarta due um 14 di 42 INT 1CH, INT 21H, INT 61H e INT 62H. L’ultima azione è un salto a BUSY_LOOP$. All’arrivo del primo tick, il loop è interrotto e il controllo passato alla prima applicazione presente nel buffer READY. File MTRUN.ASM ;DEFINIZIONE COSTANTI tick_max equ 04h ;tick in un time slice clock_max equ 08h ;frequenza richiamo routine orologio mbx_len equ 100h ;lunghezza mailbox 256 byte mbx_num equ 04h ;numero mailbox 4 kbuf_len equ 4ah ;lunghezza buffer tastiera 74 byte kbd_prompt equ 220 ;prompt “\” kbd_timeout equ 20h ;timeout per input da tastiera ;DEFINIZIONE MACRO end_process macro return_code ;funzione di terminazione del processo e mov al,return_code ;passaggio a MS-DOS di un codice di mov ah,4ch ;errore int 21h endm byte_to_dec macro ;conversione di un byte 0..99 contenuto in AL xor ah,ah ;nei char ASCII equivalenti in AH e AL mov bl,10 ;AL modulo 10 div bl ;quoziente in AL resto in AH add ax,3030h ;conversione in ASCII endm increment_AL macro ;incrementa di uno il contenuto di AL local cont_1 inc al cmp al,08h ;if (AL==8) AL=0 jb cont_1 xor al,al cont_1: endm ;AREA DATI DSEG SEGMENT proc_name db 64 dup (0) ;nome dei processi save_stack dw 16 dup (0) ;struttura dati per i pointer allo stack dei processi ready db 8 dup (0) ;buffer circolare dei processi current dw 0 ;pointer al programma corrente ready_next dw 0 ;pointer al nuovo programma ready_cnt db 0 ;numero programmi in ready disposal_mode db 0 ;modo proseguimento processo busy_state db 0 ;se=1 presenza in BUSY_LOOP$ null_state db 0 ;se=1 proc. nullo DOS_level dw 0 ;livello di chiamata DOS Semaphore db 1 ;semaforo=1 sem_wait db 8 dup (0) ;buffer circolare processi sem_first dw 0 ;pointer primo programma in attesa sem_next dw 0 ;pointer ultimo programma in attesa sem_cnt db 0 ;numero programmi in sem_wait time_left dw 64 dup (0) ;tempi attesa rimanenti time_wait db 8 dup (0) ;coda ordinata processi time_cnt db 0 ;numero programmi in time_wait Laboratorio Quarta due um 15 di 42 time_interv dw 0 ;tempo attesa richiesto in tick mbx_area db mbx_len*mbx_num dup (0) ;area messaggi in mbx mbx_ptr dw mbx_num dup (?) ;pointer posizione mailbox mbx_msgs dw mbx_num dup (0) ;numero messaggi nelle mailbox mbx_space dw mbx_num dup (mbx_len) ;byte liberi nelle mailbox mbx_first dw mbx_num dup (0) ;pointer inizio area messaggio mbx_next dw mbx_num dup (0) ;pointer inizio area libera tick dw 0 ;contatore interno clock_cnt db 0 ;richiamo routine orologio hours db 0 ;valore corrente ora minutes db 0 ;valore corrente minuti inp_prompt db ‘INPUT>‘ ;prompt di imput kbd_buf db kbuf_len dup (0) ;buffer tastiera kbd_ptr db 0 ;pointer nuovo carattere imput kbd_lock db 0 ;input bloccato per kbd_lock=1 kbd_request db 0 ;flag richiesta servizio tastiera kbd_input db 0 ;flag stato input tastiera kbd_exit db 0 ;flag uscita routine tastiera kbd_tick db 0 ;contatore per timeout tastiera status_str db “00:00:00 EXEC= SEM= WAIT= “ null_name db “--NULL--” ;processo nullo kbd_name db “KEYBOARD” ;processo ingresso tastiera load_text db ‘Loading : ‘ ;indicatore caricamento file load_file db 20h dup (0) ;stringa ASCIZ per nome file load_text_tail db 13,10,’$’ ;CR, LF e termine stringa load_ext db ‘.EXE’ ;default file di tipo .EXE cmd_ptr dw 81h ;posizione lettura comando input cmd_left dw 0 ;byte rimasti in comando input file_handle dw 0 ;handle per file da caricare file_header db 1Ch dup (0) ;buffer per file header par_block db 04h dup (0) ;parametri programma default_ext db 0 ;se=1 per default .EXE local_PSP dw 0 ;PSP di MTRUN proc_total db 0 ;numero di processi installati req_mem dw 0 ;memoria richiesta area_ptr dw 0 ;pointer area libera di memoria load_ptr dw 0 ;pointer per caricamento processo org_SS dw 0 ;SS di MTRUN org_SP dw 0 ;SP di MTRUN new_SS dw 0 ;nuovo valore calcolato per SS new_SP dw 0 ;nuovo valore calcolato per SP new_CS dw 0 ;nuovo valore calcolato per CS new_IP dw 0 ;nuovo valore calcolato per IP DSEG ENDS ;SEGMENTO VIDEO OVERLAY VIDSEG SEGMENT AT 0B800h ;monocromatico 0B000h org 80 ;segmento video overlay disp_str_1 db ? ;stato sistema in status_str org 3680 disp_str_2 db ? ;stringa di ingresso tastiera VIDSEG ENDS ;SEGMENTO STACK SSEG SEGMENT STACK ;stack locale per MTRU dw 400h dup (?) ;1K byte Laboratorio Quarta due um 16 di 42 SSEG ENDS ;SEGMENTO CODICE CSEG SEGMENT assume cs:cseg,ds:dseg,es:dseg,ss:sseg start: jmp initialize ;salto alla parte di inizializzazione ;GESTIONE CLOCK 1CH: OPERAZIONI INIZIALI ;questa routine è chiamata dal temporizzatore di sistema ;il processo int1C_handler deve completare le funzioni del temporizzatore di sistema, ;inviando un segnale di abilitazione al PIC 8259A int1C_handler: cli assume ds:dseg,es:dseg ;area dati mov ax,dseg mov ds,ax mov es,ax inc tick inc clock_cnt cmp kbd_input,0 ;durante READ_KEYBOARDS$ occorre je cont_1 ;incrementare anche kbd_tick inc kbd_tick jmp cont_2 cont_1: mov ah,01h int 16h ;è stato premuto un tasto jz cont_2 ;zero flag=clear, nessun tasto mov kbd_request,1 ;richiesta servizio tastiera cont_2: pop ax ;rimozione dei pointer al temporizzatore pop ax ;di sistema (IP, CS, Flags) pop ax cmp busy_state,0 ;se l’interruzione ha avuto luogo durante je cont_3 ;un BUSY_LOOP$ (=1) i pointer e le var pop ax ;relative vanno eliminati dallo stack pop ax pop ax pop ax pop ax pop ax mov busy_state,0 jmp cont_4 cont_3: cmp kbd_tick,kbd_timeout ;se è trascorso il timeout per jb cont_4 ;ingresso dati da tastiera pop ax ;(kbd_tick=kbd_timeout), i pointer e le variabili di pop ax ;READ_KEYBOARD$ vanno eliminati dallo stack pop ax pop ax pop ax pop ax mov kbd_tick,0 ;azzeramento timeout tastiera mov kbd_input,0 ;uscita da READ_KEYBOARD$ mov kbd_exit,1 jmp cont_5 ;non è necessario salvare i registri Laboratorio Quarta due um 17 di 42 cont_4: cmp null_state,0 ;se l’interruzione ha avuto luogo durante un ja cont_5 ;null_state (=1) non è necessario salvare i registri push bx ;i registri rimanenti del processo originale vanno push cx ;salvati su stack push bp push si push di push es cont_5: sti mov al,20h ;EOI per 8259A out 20h,al ;riabilitazioni interruzioni cmp DOS_level,0 ;proseguimento con update_clock je update_clock ;solo se DOS_level=0 jmp display_system_data ;GESTIONE CLOCK 1CH: AGGIORNAMENTO OROLOGIO update_clock: cmp clock_cnt,clock_max ;richiamo routine solo ogni clock_max tick jae cont_6 jmp display_system_data cont_6: mov clock_cnt,0 mov ah,0h ;servizio BIOS ora sistema int 1ah ;richiamo, risultato (numero di tick) in CX:DX mov ax,dx ;parte bassa in AX mov dx,cx ;parte alta in DX shr dx,1 ;DX/2 shr ax,1 ;AX/2 and cl,00000001b ;se c’è riporto nella aprte alta, trasporto in AX cmp cl,0 je cont_7 add ah,10000000b cont_7: mov cx,32772 ;65543 tick/ora =2*32772 div cx cmp ax,24 ;DX:AX/CX risultato in AX jb cont_8 ;azzeramento se ore >= 24 xor ax,ax cont_8: mov hours,al mov ax,dx ;resto della prima divisione xor dx,dx mov cx,546 ;1092 conteggi/minuto =2*546 div cx ;DX:AX/AX risultato in AX cmp ax,60 jb cont_9 ;azzeramento se minuti >= 60 xor ax,ax cont_9: mov minutes,al mov ax,dx ;resto della seconda divisione xor dx,dx mov cx,10 ;18.2 conteggi/secondo=91*2/10 mul cx ;AX*CX risultato in Dx:Ax Laboratorio Quarta due um 18 di 42 mov cx,91 div cx ;DX:AX/CX risultato in AX cmp ax,60 jb cont_10 ;azzeramento se secondi >= 60 xor ax,ax cont_10: byte_to_dec ;conversione di secondi, minuti e ore in ASCII per mov status_str[6],al ;presentazione su schermo mov status_str[7],ah mov al,minutes byte_to_dec mov status_str[3],al mov status_str[4],ah mov al,hours byte_to_dec mov status_str[0],al mov status_str[1],ah ;GESTIONE CLOCK 1CH: DISPLAY STATO PROCESSO E STRINGA INGRESSO display_system_data: cld ;direzione scambio byte mov si, offset status_str ;DS:SI punta status_str mov ax,vidseg ;ES=VIDSEG segmento video mov es,ax assume es:vidseg mov di,offset disp_str_1 ;ES:DI punta alla destinazione in area video mov cl,40 ;spostamento di 40 byte mov al,112 ;codice per colore inverso loop_1: movsb ;carattere da status_str stosb ;codice colore da AL dec cl cmp cl,0 ja loop_1 display_input_string: mov si,offset inp_prompt ;DS:SI punta a inp_prompt mov di,offset disp_str_2 ;ES:DI punta alla destinazione in area video mov cl,80 ; spostamento di 80 byte loop_2:movsb ;caratteri da inp_prompt e kbd_buf stosb ;codice colore in AL dec cl cmp cl,0 ja loop_2 assume es:dseg mov ax,dseg ;ripristino ES=DSEG originale mov es,ax ;GESTIONE CLOCK 1CH: AGGIORNAMENTO CODA DI ATTESA cmp time_cnt,0 ;se non ci sono processi in attesa (time_cnt=0) je check_DOS_level ;salto al modulo seguente mov dx,time_left ;se il primo intervallo di attesa è zero cmp dx,0 ;il processo va richiamato je awake_process dec dx mov time_left,dx ;nuovo intervallo in tick jmp check_DOS_level ;uscita awake_process: ;solo il primo processo in attesa è richiamato Laboratorio Quarta due um 19 di 42 mov bl,time_wait ;numero del primo processo mov si,ready_next ;nuovo processo in ready mov ready[si],bl mov ax,si ;incremento pointer buffer ready increment_AL ;azzeramento di AL quando AL=8 mov ready_next,ax dec time_cnt ;processi in attesa -1 inc ready_cnt ;processi pronti +1 cmp time_cnt,0 ;uscita se non è rimasto nessun processo je check_DOS_level xor cx,cx shift_queue_left: cmp cl,time_cnt ;uscita se non sono rimasti processi da spostare je check_DOS_level mov si,cx inc si ;punta al processo successivo mov di,cx mov bh,time_wait[si] ;il numero del processo mov time_wait[di],bh ;è spostato a sinistra shl si,1 ;SI*2 shl di,1 ;DI*2 mov bx,time_left[si] ;il tempo di attesa è spostato a sinistra mov time_left[di],bx inc cx ;processo successivo jmp shift_queue_left ;GESTIONE CLOCK 1CH: SCAMBIO PROCESSI (SCHEDULER) check_DOS_level: cmp DOS_level,0 ;se DOS_level >0 il processo je check_kbd_input ;non dev’essere fermato jmp restore_context check_kbd_input: cmp kbd_input,0 ;se kbd_input=1 il processo è READ_KEYBOARD$ je check_kbd_exit ;e non dev’essere fermato jmp restore_context check_kbd_exit: cmp kbd_exit,0 ;kbd_exit=1 indica uscita da READ_KEYBOARD$ je check_null_state ;l’esecuzione continua da load_new_process mov kbd_exit,0 jmp load_new_process check_null_state: cmp null_state,0 ;se null_state=1 il processo non si salva je check_disposal jmp load_new_process check_disposal: cmp disposal_mode,0 ;se disposal_mode>=1 il proceso va cambiato ja save_stack_ptrs cmp kbd_request,0 ;se kbd_request=1 il processo è fermato e il ja save_stack_ptrs ;controllo passa a READ_KEYBOARD$ cmp tick,tick_max ;se tick>tick_max il processo va cambiato jae save_stack_ptrs jmp restore_context ;SALVATAGGIO PUNTATORI STACK DEL PROCESSO INTERROTTO save_stack_ptrs: Laboratorio Quarta due um 20 di 42 mov tick,0 ;azzeramento tick mov si,current mov al,ready[si] ;numero del processo attuale shl al,1 ;pointer a save_stack=AL*4 shl al,1 ;AL*2 xor ah,ah mov si,ax ;SI= pointer a save_stack cli mov ax,sp ;SP stack processo corrente mov save_stack[si],ax mov ax,ss ;SS stack processo corrente mov save_stack[si+2],ax mov ax,org_SS ;ripristino stack originale del processo MTRUN mov ss,ax mov ax,org_SP mov sp,ax dec ready_cnt ;processi pronti-1 sti proc_disposal: mov al,disposal_mode ;in disposal_mode è specificato se il processo mov disposal_mode,0 ;corrente: - deve attendere un semaforo cmp al,01h ;(disposal_mode=1) je put_sem_wait ;- o per un intervallo di tempo (disposal_mode=3) cmp al,03h ;- se è pronto (disposal_mode=0) je put_time_wait ;IL PROCESSO INTERROTTO È MESSO NELLA CODA DEI PROCESSI PRONTI put_ready: mov si,current ;il numero del processo corrente mov bl,ready[si] ;è tolto da ready mov si,ready_next ;nuovo processo in coda ready mov ready[si],bl ;BL = numero del processo mov ax,si increment_AL ;azzeramento di AL quando AL = 8 mov ready_next,ax inc ready_cnt ;processi pronti +1 jmp load_new_process ;IL PROCESSO INTERROTTO È MESSO IN CODA ATTESA SEMAFORO put_sem_wait: mov si,current ;il numero del processo mov bl,ready[si] ;corrente è tolto da ready mov si,sem_next ;nuovo processo in coda semaforo mov sem_wait[si],bl ; BL = numero del processo mov ax,si increment_AL ;azzeramento di AL quando AL = 8 mov sem_next,ax inc sem_cnt ;processi attesa semaforo +1 jmp load_new_process ;IL PROCESSO INTERROTTO È MESSO IN CODA DI ATTESA INTERVALLO TEMPO put_time_wait: xor cx,cx ;CL serve da indice per i processi in coda mov dx,time_interv ;intervallo di attesa per il nuovo processo check_insert: ;controllo punto inserimento nuovo processo cmp cl,time_cnt ;a ogni passaggio è ricalcolato il tempo relativo di je insert_new_process ;attesa Laboratorio Quarta due um 21 di 42 mov si,cx ;pointer a intervallo successivo shl si,1 ;SI * 2 mov ax,time_left[si] cmp ax,dx ;confronto intervalli di tempo ja shift_queue_right sub dx,ax ;nuovo intervallo relativo inc cx ;prova col processo successivo jmp check_insert shift_queue_right: mov al,time_cnt ;AX contiene la cella di destinazione mov ah,0 loop_3: cmp al,cl ;destinazione = inizio? je new_relative_wait mov si,ax dec si mov di,ax mov bh,time_wait[si] ;il numero del processo è spostato verso destra mov time_wait[di],bh shl si,1 ; SI * 2 shl di,1 ; DI * 2 push bx mov bx,time_left[si] ;il tempo attesa è spostato verso destra mov time_left[di],bx pop bx dec ax jmp loop_3 new_relative_wait: sub time_left[di],dx ;tempo attesa processo seguente insert_new_process: mov si,current ;il numero del processo corrente è tolto da ready mov bl,ready[si] mov si,cx mov time_wait[si],bl ;nuovo processo in attesa shl si,1 ;SI * 2 mov time_left[si],dx ;nuovo tempo relativo attesa inc time_cnt ;processi in attesa tempo + 1 ;CARICAMENTO DI UN NUOVO PROCESSO ;a questo punto è controllato se un processo è pronto nel buffer ready (ready_cnt>0). Se ;un processo è disponibile, esso è caricato e il suo nome mostrato sullo schermo. Se ;nessun processo è pronto, il controllo passa a BUSY_LOOP$, nel qual caso sullo ;schermo è mostrato il nome “—NULL—”. load_new_process: cld mov al,semaphore ;valore attuale semaforo per presentazione su schermo add al,30h ;conversione ASCII mov status_str[1Fh],al ;offset in status_str = 31 mov al,time_cnt ;numero processi in wait_time add al,30h ;conversione ASCII mov status_str[27h],al ;offset in status_str = 39 mov null_state,0 ;indica un processo regolare (non BUSY_LOOP$) cmp kbd_request,0 ;passaggio a READ_KEYBOARD$ ja read_keyboard$ ;se un carattere è in attesa cmp ready_cnt,0 ;se ready_cnt=0 non ci sono processi Laboratorio Quarta due um 22 di 42 je null_process ;pronti, il controllo è passato a BUSY_LOOP$ mov ax,current ;AX=pointer al buffer ready increment_AL ;azzeramento di AL quando AL = 8 mov current,ax ;nuovo pointer buffer ready mov si,ax ;presentazione su schermo del nome nuovo processo mov al,ready[si] ;numero del nuovo processo shl al,1 ;AL * 2 shl al,1 ;AL * 2 shl al,1 ;AL * 2 xor ah,ah ;il puntatore a proc_name add ax,offset proc_name ;è uguale a AL*8 mov si,ax ;DS:SI punta a proc_name mov ax,offset status_str add ax,12h ;offset in status_str = 18 mov di,ax ;ES:DI punta a status_str mov cx,08h ;spostamento a 8 byte rep movsb get_stack_ptrs: ;ripristino pointer stack mov si,current ;pointer al nuovo processo mov al,ready[si] ;numero del nuovo processo shl al,1 ;AL * 2 shl al,1 ;AL * 2 xor ah,ah ;il pointer a save_stack mov si,ax ;è uguale a AL*4 cli mov ax,save_stack[si] ;SP stack nuovo processo mov sp,ax mov ax,save_stack[si+2] ;SS stack nuovo processo mov ss,ax sti ;RICHIAMO DEL NUOVO PROCESSO restore_context: cli pop es ;ripristino stack e ritorno al processo originale pop di pop si pop bp pop cx pop bx pop dx pop ax pop ds sti iret ;**** passaggio controllo al nuovo processo ;PROCESSO NULLO null_process: mov null_state,1 ;indica processo di attesa mov si,offset null_name ;copiatura come “NULL” mov di,offset status_str ;in status_str add di,18 ;offset in status_str = 18 mov cx,8 ;spostamento a 8 byte rep movsb ;BUSY_LOOP$ CICLO DI ATTESA busy_loop$: Laboratorio Quarta due um 23 di 42 mov busy_state,1 ;indica presenza in BUSY_LOOP$ sti loop$: jmp loop$ ;continua fino a interruzione al prossimo tick di sistema ;READ_KEYBOARD$ INGRESSO DI UN CARATTERE DA TASTIERA read_keyboard$: mov kbd_request,0 ;azzeramento richiesta servizio mov kbd_input,1 ;indica presenza in kbd routine mov si,offset kbd_name ;DS:SI punta a kbd_name mov di,offset status_str ;ES:DI punta a status_str add di,12h ;offset in status_str = 18 mov cx,08h ;spostamento a 8 byte rep movsb read_char: mov ah,01h ;BIOS read keyboard status int 16h ;se c’è un nuovo carattere jz read_char ;zero flag=clear mov kbd_tick,0 ;azzeramento input timeout per nuovo carattere mov ah,00h ;BIOS read character int 16h ;codice ASCII del carattere è ritornato in AL cmp kbd_lock,0 ;la lettura è libera solo per ja read_char ;kbd_lock=0 cmp al,08h ;carattere=backspace? je process_BS ;elaborazione particolare cmp al,0Dh ;carattere = <return>? je process_CR ;elaborazione particolare cmp al,20h ;carattere ASCII < 32? jb read_char ;nessuna elaborazione: uscita cmp al,7Fh ;carattere ASCII > 127? ja read_char ;nessuna elaborazione: uscita cmp kbd_ptr,kbuf_len ;uscita se è stato raggiunto il numero jae read_char ;massimo di caratteri mov bx,offset kbd_buf ;scrittura carattere in buffer xor cx,cx ;azzeramento CX mov cl,kbd_ptr add bx,cx mov di,bx ;pointer assoluto a buffer stosb ;il carattere input è AL inc cl ;muovi pointer a destra mov kbd_ptr,cl cmp kbd_ptr,kbuf_len ;uscita se è stato raggiunto il numero jae read_char ;massimo di caratteri mov al,kbd_prompt ;prompt per input tastiera stosb ;su schermo jmp read_char process_BS: ;carattere backspace cmp kbd_ptr,0 ;uscita immediata se ci si je read_char ;trova all’inizio di kbd_buf mov bx,offset kbd_buf ;pointer a kbd_buf mov cl,kbd_ptr xor ch,ch ;azzeramento CH dec cl ;muovi pointer a sinistra add bx,cx mov di,bx ;pointer assoluto a buffer mov al,kbd_prompt ;prompt per input tastiera Laboratorio Quarta due um 24 di 42 stosb mov kbd_ptr,cl cmp cl,kbuf_len-1 ;interruzione se è stato raggiunto il jae read_char ;numero massimo di caratteri mov al,0 ;cancellazione carattere stosb ;su schermo jmp read_char process_CR: ;carattere CR mov kbd_lock,1 ;disabilitazione nuovi ingressi mov kbd_tick,kbd_timeout ;il timeout non serve più kbd_busy$: ;in attesa della prossima interruzione jmp kbd_busy$ ;del timer di sistema ;GESTIONE INTERRUZIONI INT 62H FUNZIONI KERNEL ;L’INT 62H serve come richiamo generale ai servizi forniti dal kernel: gestione del ;semaforo, degli intervalli di attesa, dello scambio messaggi per mailbox, lettura ora ;interna e della stringa in ingresso da tastiera. ;I parametri richiesti dalle diverse funzioni sono passati tramite i registri. ;Il codice della funzione scelta è posto in AH. ;01h semaphore signal ;02h semaphore wait_sem ;03h wait_time ;04h get_time ;05h put_mailbox ;06h get_mailbox ;07h read_string, lettura stringa da tastiera int62_handler: cli ;vanno sempre salvati push ds push bx assume ds:dseg ;riferimento comune per tutte le funzioni mov bx,dseg mov ds,bx ;DS=DSEG via BX inc DOS_level ;per evitare preemption durante questa procedura sti cmp ah,01h ;controllo via AH della routine destinazione je func_01 cmp ah,02h je func_02 cmp ah,03h ja cont_11 jmp func_03 cont_11: cmp ah,04h ja cont_12 jmp func_04 cont_12: cmp ah,05h ja cont_13 jmp func_05 cont_13: cmp ah,06h ja cont_14 jmp func_06 cont_14: Laboratorio Quarta due um 25 di 42 cmp ah,07h ja cont_15 jmp func_07 cont_15: jmp exit_int62 ;INT 62H, FUNZIONE 01, SEMAFORO ‘SIGNAL’ func_01: cmp sem_cnt,0 ;controllo numero processi in attesa ja release_proc inc semaphore ;se nessun processo è in attesa, incremento jmp exit_int62 ;semaforo e uscita release_proc: ;rilascio primo processo in attesa push ax push si mov si,sem_first ;pointer al primo dei processi in attesa mov bl,sem_wait[si] ;numero del processo mov ax,si increment_AL ;azzeramento di AL quando AL=8 mov sem_first,ax mov si,ready_next ;pointer al buffer ready mov ready[si],bl ;numero del processo mov ax,si ;incremento del puntatore AL increment_AL ;azzeramento di AL quando AL=8 mov ready_next,ax dec sem_cnt ;processi in attesa - 1 inc ready_cnt ;processi pronti + 1 pop si ;ripristino registri utilizzati pop ax jmp exit_int62 ;INT 62H, FUNZIONE 02, SEMAFORO ‘WAIT_SEM’ func_02: cmp semaphore,0 ;se il semaforo ha valore 0 je process_wait ;il processo deve aspettare dec semaphore ;il semaforo è > 1 jmp exit_int62 ;decremento e uscita process_wait: ;inserimento del processo in coda attesa mov disposal_mode,1 ;codice di proseguimento jmp jmp_busy$ ;per scambio processi ;INT 62H, FUNZIONE 03, ATTESA ‘WAIT_TIME’ func_03: mov disposal_mode,3 ;codice di proseguimento per scambio processi mov time_interv,dx ;tempo di attesa in tick jmp jmp_busy$ ;INT 62H, FUNZIONE 04, LETTURA ORA ‘GET_TIME’ func_04: push cx ;l’ora attuale del sistema push si ;è all’inizio di status_str mov si,offset status_str ;DS:SI punta a status_str mov cx,08h ;8 byte da muovere rep movsb ;ES:DI punta alla stringa di destinazione pop si pop cx jmp exit_int62 ;INT 62H, FUNZIONE 05, SCRITTURA IN MAILBOX ‘PUT_MAILBOX’ Laboratorio Quarta due um 26 di 42 ;AL contiene il numero della mailbox, ES:DI è il pointer alla stringa da trasferire nella ;mailbox; il primo byte della stringa indica la lunghezza del suo messaggio. func_05: cmp al,mbx_num-1 ;controllo numero mbx jbe check_mbx_space mov ax,4 ;codice errore = 4 jmp exit_int62 ;numero mailbox errato check_mbx_space: ;controllo spazio disponibile nella mailbox selezionata mov ah,0 ;AX = numero mailbox shl al,1 ;AL * 2 mov di,ax ;DI = pointer strutture dati mailbox mov ax,mbx_space[di] ;spazio disponibile in mbx(n) xor cx,cx mov cl,es:[si] ;byte d’indicazione lunghezza nella stringa originale inc cx ;per il primo byte di lunghezza cmp ax,cx jae update_pointers_05 mov ax,1 ;codice errore = 1 jmp exit_int62 ;spazio insufficiente in mbx(n) update_pointers_05: ;aggiornamento strutture dati sub ax,cx mov mbx_space[di],ax ;nuovo spazio disponibile inc mbx_msgs[di] ;numero messaggi in mbx[DI] + 1 mov ax,mbx_next[di] ;pointer inizio area libera in mbx mov bx,ax ;BX=pointer destinazione stringa in mbx add ax,cx xor dx,dx cmp ax,mbx_len ;controllo se il pointer alla jb cont_16 ;nuova area libera supera la sub ax,mbx_len ;lunghezza della mailbox e va mov dx,ax ;riferito di nuovo all’inizio cont_16: mov mbx_next[di],ax ;nuovo pointer inizio area libera ;Contenuto registri. ;BX = pointer area destinazione messaggio in mbx. ;CX = lunghezza totale messaggio (compreso byte indicazione lunghezza). ;DX = lunghezza messaggio da inizio mailbox. ;DI = pointer strutture dati per mailbox (0..mbx_num-1)*2. ;ES:SI = punta al messaggio originale. store_msg: ;scrittura messaggio in mailbox push di add bx,mbx_ptr[di] ;BX= pointer assoluto per destinazione in mailbox pop di ;DI=pointer strutture dati mailbox mov ax,mbx_ptr[di] ;pointer assoluto inizio mbx mov di,bx ;destinazione in mailbox mov bx,ax ;pointer assoluto inizio mbx mov ax,es ;DS=ES via AX mov ds,ax ;DS:SI = messaggio sorgente mov ax,dseg ;ES=DSEG via AX mov es,ax ;ES:DI = destinazione in mbx sub cl,dl ;trasferimento parte messaggio rep movsb ;da pointer a termine mailbox mov di,bx ;ES:DI = nuova destianzione in mbx mov cl,dl ;parte rimanente del messaggio Laboratorio Quarta due um 27 di 42 rep movsb ;trasferimento xor ax,ax ;codice errore = 0 ,risultato OK assume ds:dseg ;ripristino DS originale mov bx,dseg ;DS=DSEG via BX mov ds,bx jmp exit_int62 ;INT 62H, FUNZIONE 06, LETTRUA DA MAILBOX ‘GET_MAILBOX’ ;AL contiene il numero della mailbox. ;ES:DI è il pointer alla stringa di destinazione del messaggio. func_06: cmp al,mbx_num-1 ;controllo numero mailbox jbe check_msg mov ax,4 ;codice errore = 4 jmp exit_int62 ;numero mailbox errato check_msg: ;controllo presenza messaggi nella mailbox selezionata mov ah,0 ;AX = numero mailbox shl al,1 ;AL * 2 mov si,ax ;SI = pointer strutture dati mailbox cmp mbx_msgs[si],0 ;numero messaggi in mbx(n) ja update_pointers_06 mov ax,2 ;codice errore = 2 jmp exit_int62 ;nessun messaggio in mbx(n) update_pointers_06: dec mbx_msgs[si] ;numero messaggi in mbx[DI] - 1 mov ax,mbx_first[si] ;pointer a primo messaggio in mailbox mov bx,ax add bx,mbx_ptr[si] ;pointer assoluto a primo messaggio push si ;SI = pointer strutture dati mailbox mov si,bx xor cx,cx mov cl, [si] ;il primo byte del messaggio indica la lunghezza inc cx ;per il primo byte di lunghezza pop si ;SI = pointer strutture dati mailbox add ax,cx xor dx,dx cmp ax,mbx_len ;se (AX<mbx_len), il messaggio non è spezzato jb cont_17 ;nella mailbox sub ax,mbx_len ;AX, DX = numero byte del messaggio mov dx,ax ;all’inizio della mailbox cont_17: mov mbx_first[si],ax ;puntatore messaggio successivo add mbx_space[si],cx ;nuovo spazio disponibile ;Contenuto registri. ;BX = pointer assoluto primo messaggio in mailbox. ;CX = lunghezza totale messaggio (compreso byte indicazione lunghezza). ;DX = lunghezza parte messaggio da inizio mailbox. ;SI = pointer strutture dati per mailbox (0..mbx_num-1)*2. ;ES:D= pointer alla stringa di destinazione messaggio. get_msg: ;lettura messaggio da mailbox push si mov si,bx ;pointer assoluto a primo messaggio sub cl,dl rep movsb ;trasferimento prima parte Laboratorio Quarta due um 28 di 42 pop si ;SI = pointer strutture dati per mailbox mov ax,mbx_ptr[si] ;pointer assoluot inizio mailbox mov si,ax ;DS:DI = nuova destinazione in stringa mov cl,dl rep movsb ;trasferimento seconda parte xor ax,ax ;codice errore = 0, risultato OK jmp exit_int62 ;INT 62H, FUNZIONE 07, LETTURA STRINGA INGRESSO TASTIERA ‘READ_STRING’ ;ES:DI = pointer alla stringa di destinazione. func_07: cmp kbd_lock,0 ;<CR> è stato premuto? ja copy_string ;se si, la stringa va copiata mov ax,2 ;codice errore = 2 nessun ingresso a tastiera mov es:[di],word ptr 0 ; stringa di lunghezza = 0 jmp exit_int62 copy_string: mov si,offset kbd_buf ;pointer buffer di input xor cx,cx mov al,kbd_ptr ;numero byte da trasferire mov cl,al stosb ;primo byte destinazione=lunghezza della stringa in input rep movsb ;copiatura stringa input numero byte in CL mov cl,kbuf_len ;numero byte da azzerare mov ax,ds ;ES=DS via AX mov es,ax mov di,offset kbd_buf ;destinazione in ES:DI xor al,al rep stosb ;azzeramento buffer input mov kbd_ptr,0 ;azzeramento pointer kbd buffer mov di,offset kbd_buf ;destinazione in ES:DI mov al,kbd_prompt ;prompt per input da tastiera su schermp stosb mov kbd_lock,0 ;riabilitazione input da tastiera xor ax,ax ;codice errore=0 risultato OK jmp exit_int62 ;INT 62H, TERMINE PROCEDURA exit_int62: dec DOS_level ;riabilitazione preemption cli pop bx pop ds sti iret ;ritorno a processo chiamante jmp_busy$: ;salto a busy-loop$ cli pop bx push ax ;restano da salvare per il ripristino del contesto push dx dec DOS_level ;riabilitazione preemption jmp busy_loop$ ;PROGRAMMA DI GESTIONE DOS INTERRUPT 21H ;Questa parte di programma serve a due scopi: ;1. Identificare tramite DOS_level che ci si trova all’interno di una chiamata DOS per cui Laboratorio Quarta due um 29 di 42 ;non è possibile interrompere l’esecuzione del processo corrente, ;2. filtrare le chiamate alla funzione di allocazione blocchi di memoria, restituendo una ;risposta fittizia di errore. int21_handler: push ds push bx assume ds:dseg mov bx,dseg ;DS=DSEG via BX mov ds,bx inc DOS_level pop bx pop ds cmp ax,4ah ;le chiamate alla funzione setblock vanno filtrate je mask_4a int 61h ;NUOVO RICHIAMO INTERRUPT (21H) jmp cont_18 mask_4A: mov ax,08h ;codice fittizio di errore = memoria insufficiente stc ;carry set =>errore simulato cont_18: pushf ;le flag di ritorno da DOS vanno salvate push ds push bx mov bx,dseg ;DS=DSEG via BX mov ds,bx dec DOS_level pop bx ;ripristino registri originali in uscita DOS pop ds popf iret ;ritorno a programma chiamante ;PARTE INIZIALE CARICAMENTO PROGRAMMI ;All’inizio dell’esecuzione DS ed ES puntano all’area PSP. La posizione di quest’area è ;salvata in local_PSP, la lunghezza della coda di comando (byte 80H di PSP) in cmd_left e ;DS è indirizzato a DSEG. initialize: mov si,80h ;posizione lunghezzacoda di comando lodsb ;lettura primo carattere in AL mov ah,0h mov cx,ax ;lunghezza coda comando in CX mov ax,ds ;local_PSP = PSP di MTRUN mov bx,dseg ;DS=DSEG via BX mov ds,bx assume ds:dseg mov local_PSP,ax ;segmento PSP di MTRUN mov cmd_left,cx ;lunghezza coda di comando in ingresso reduce_memory: ;riduzione della memoria allocata a MTRUN mov ah,4ah ;funzione MS-DOS Set_Block mov bx,400h ;riduzione a 1000 blocchi ES=PSP di MTRUN int 21h jnc file_select ;proseguimento se tutto OK (bit carry =0) end_process 1 ;terminazione esecuzione ;INTERPRETAZIONE LINEA ORIGINALE DI COMANDO ESTRAZIONE NOME FILE ;Il nome del file da caricare è letto dalla linea originale di comando in load_file,il solo nome ;è copiato in proc_name. Se l’stensione non è indicata, è presa di default .EXE. Laboratorio Quarta due um 30 di 42 file_select proc far mov di,offset load_file mov ax,dseg mov es,ax mov al,0 mov cx,20h rep stosb mov default_ext,1 read_tail_next: mov di,offset load_file mov si,cmd_ptr mov cx,cmd_left mov ax,local_PSP mov ds,ax loop_4: lodsb dec cx cmp al,20h jbe loop_4 stosb loop_5: lodsb dec cx cmp al,2Eh jne cont_19 mov es:default_ext,0 cont_19: cmp al,2Ch je save_pointers stosb cmp cx,0 ja loop_5 save_pointers: mov ax,dseg mov ds,ax assume ds:dseg; es:dseg mov cmd_ptr,si mov cmd_left,cx append_default: cmp default_ext,0 je copy_proc_name mov si,offset load_ext mov cx,04h rep movsb copy_proc_name: mov al,proc_total mov cl,08h mul cl add ax,offset proc_name mov di,ax mov si,offset load_file loop_6: lodsb cmp al,2Eh je display_file_name cmp al,61h jb cont_20 Laboratorio Quarta due ;operazioni generali ;indirizzo di destinazione ;ES=DSEG via AX ;ES:DI punta a load_file ;azzeramento stringa load_proc ;va presa l’estensione di default ;lettura file successivo ;ES:DI punta a load_file ;byte rimasti da leggere ;DS= local_PSP via AX ;DS:SI punta a comando in input ;caratteri da leggere - 1 ;se il carattere non è valido (codice ASCII<=32), ; leggi prossimo, altrimenti memorizza e procedi ;copiatura in load_file ;caratteri da leggere - 1 ;se il carattere è ASCII “.” non occorre prendere ;l’estensione di default ;se il carattere è ASCII “,”, interruzione lettura ;copiatura nuovo carattere ;leggi il carattere successivo ;se la stringa non è terminata ;DS va ripristinato per puntare al segmento dati ;ES punta già a DSEG ;nuova posizione in input ;byte rimasti da leggere nel comando originale ;se default_ext=1 è necessario aggiungere ;l’estensione “.EXE” a proc_name ;lunghezza 4 caratteri ;calcolo del pointer a proc_name ;= offset + (proc_total * 8) ;ES:DI punta a proc_name ;DS:SI punta a load_file ;la copiatura va interrotta se il carattere e’ “.” ;(ASCII 2Eh) ;conversione caratteri minuscoli in maiuscoli um 31 di 42 sub al,20h cont_20: stosb ;copiatura nuovo carattere jmp loop_6 file_select endp ;PRESENTAZIONE SU SCHERMO DEL NOME FILE CARICATO display_file_name: mov dx,offset load_text mov ah,09h ;funzione DOS Display_String int 21h ;LETTURA FILE HEADER DA DISCO open_file_handle: ;apertura del file in lettura mov dx,offset load_file ;DS:DX punta a load_file mov ah,3dh ;funzione DOS Open_File mov al,0h ;modo di accesso READONLY int 21h jnc read_from_file ;proseguimento se tutto OK end_process 2 ;terminazione esecuzione read_from_file: mov file_handle,ax ;risultato chiamata da AX mov bx,ax ;handle per il file mov dx,offset file_header ;DS:DX punta a file_header mov cx,28 ;numero byte da leggere mov ah,3fh ;funzione DOS Read_File int 21h jnc close_file_handle ;proseguimento se tutto OK end_process 3 ;terminazione esecuzione close_file_handle: mov bx,file_handle mov ah,3eh ;funzione DOS Close_File int 21h jnc request_memory ;proseguimento se tutto OK end_process 4 ;terminazione esecuzione ;CALCOLO E ALLOCAZIONE MEMORIA NECESSARIA AL NUOVO PROCESSO request_memory: mov ax,word ptr file_header[4] ;dimensione del file in pagine (512 byte) mov cx,32 ;conversione in paragrafi mul cx ;AX * 32 add ax,word ptr file_header[0ah] ;memoria minima da allocare (da file_header) add ax,word ptr file_header[10h] ;spazio richiesto dallo stack (valore iniziale SP) add ax,10h ;10 paragrafi=area PSP del processo mov req_mem,ax ;memoria richiesta in paragrafi mov bx,ax ;memoria richiesta in BX mov ah,48h ;funzione DOS Allocate_Memory int 21h jnc make_new_PSP ;proseguimento se tutto OK (bit carry=0) end_process 5 ;terminazione esecuzione ;COSTRUZIONE DEL NUOVO PSP NELL’AREA ALLOCATA make_new_PSP: mov area_ptr,ax ;AX = pointer ad area libera add ax,10h ;spazio richiesto da PSP mov load_ptr,ax ;segmento di caricamento mov es,area_ptr mov di,0 ;il PSP del processo originale è copiato nel nuovo Laboratorio Quarta due um 32 di 42 mov ds,local_PSP ;DS = local_PSP (PSP di MTRUN) mov si,0h mov cx,100h ;256 byte in PSP rep movsb ;copiatura mov ax,dseg ;ripristino DS=DSEG via AX mov ds,ax ;CARICAMENTO NUOVO PROGRAMMA COME OVERLAY mov bx,load_ptr ;costruzione parameter block mov word ptr par_block[0],bx mov word ptr par_block[2],bx mov ax,dseg ;ES=DSEG via AX mov es,ax mov bx,offset par_block ;ES:BX punta a par_block mov dx,offset load_file ;DS:DX punta a load_file mov ah,4bh ;funzione DOS Load_Execute mov al,03h ;modo Load_Overlay int 21h jnc new_registers ;proseguimento se tutto OK (bit carry=0) end_process 6 terminazione esecuzione ;PREPARAZIONE DEI REGISTRI E DELLO STACK PER IL NUOVO PROGRAMMA new_registers: ;calcolo registri per il nuovo processo mov ax,word ptr file_header[0Eh];SS iniziale (è qui rilocato rispetto a inizio add ax,load_ptr ;area caricamento) mov new_SS,ax mov ax,word ptr file_header[10h] ;SP iniziale (non necessita rilocazione) mov new_SP,ax mov ax,word ptr file_header[16h] ;CS iniziale (è qui rilocato rispetto a inizio add ax,load_ptr ;area caricamento) mov new_CS,ax mov ax,word ptr file_header[14h] ;IP iniziale (non necessita rilocazione) mov new_IP,ax cli mov ax,ss ;salvataggio stack originale mov org_SS,ax mov ax,sp mov org_SP,ax mov ax,new_SS ;passaggio al nuovo stack mov ss,ax mov ax,new_SP mov sp,ax xor ax,ax mov bx,new_CS mov cx,new_IP mov dx,area_ptr pushf push bx ;nuovo CS push cx ;nuovo IP push dx ;nuovo DS push ax ;nuovo AX push ax ;nuovo DX push ax ;nuovo BX push ax ;nuovo CX push ax ;nuovo BP push ax ;nuovo SI Laboratorio Quarta due um 33 di 42 push ax ;nuovo DI push dx ;nuovo ES sti mov al,proc_total shl al,1 ;AL * 4 shl al,1 mov ah,0 mov si,ax cli mov ax,sp ;i pointer al nuovo stack sono messi in save_stack mov save_stack[si],ax mov ax,ss mov save_stack[si+2],ax mov ax,org_SS ;ripristino stack originale del processo MTRUN mov ss,ax mov ax,org_SP mov sp,ax sti ready_insert: ;inserimento nel buffer dei processi pronti mov al,proc_total ;AL = numero del processo xor ah,ah mov si,ax ;SI = pointer al processo mov ready[si],al ;inserimento nel buffer inc proc_total ;processi caricati + 1 ;CONTROLLO SE DEV’ESSERE CARICATO UN NUOVO FILE mov ax,cmd_left ;byte ancora da leggere dalla stringa di ingresso cmp ax,0 ;se AX = 0, si è al termine della linea di comando je define_interrupts jmp file_select ;caricamento di un nuovo file ;NUOVE ASSEGNAZIONI PER LE INTERRUZIONI define_interrupts: mov al,21h ;lettura pointer a INT 21H mov ah,35h ;funzione DOS Get_Interrupt_Vector int 21h mov dx,bx ;i pointer sono trasferiti da ES:BX a DS:DX mov bx,es mov ds,bx mov al,61h ;definizione di INT 61H che punta a INT 21H mov ah,25h ;funzione DOS Set_Interrupt_Vector int 21h mov bx,cs mov ds,bx ;INT 21H è riferito alla routine int21_handler mov dx,offset int21_handler mov al,21h mov ah,25h int 61h ;chiamata a DOS con la nuova mov bx,cs ;l’interruzione di servizio INT 61H è riferita alla mov ds,bx ;routine int62_handler mov dx,offset int62_handler mov al,62h ;nuova interruzione di servizio mov ah,25h int 61h mov bx,cs ;l’interruzione clock INT 1CH è riferita alla mov ds,bx ;routine int1C_handler Laboratorio Quarta due um 34 di 42 mov dx,offset int1C_handler mov al,1ch ;routine interruzione clock mov ah,25h int 61h ;CALCOLO DEI POINTER ALLE MAILBOX IN STRUTTURA MBX_PTR ;I pointer alle mailbox sono registrati nella struttuta mbx_ptr per evitare di calcolarli a ogni ;richiamo fatto da essi e per rendere il loro accesso simile a quello per le altre strutture dati define_mbx_ptr: mov ax,dseg ;DS=DSEF via AX mov ds,ax assume ds:dseg xor cx,cx xor di,di mov ax,offset mbx_area ;pointer a malibox 0 (1) loop_7: mov mbx_ptr[di],ax ;il ciclo ha termine quando tutti i pointer inc cx ;sono stati scaricati cmp cx,mbx_num je cont_21 add ax,mbx_len ;pointer mailbox successiva add di,2 ;nuovo indice per mbx_ptr jmp loop_7 cont_21: ;DEFINIZIONE VARIABILI RELATIVE AI PROCESSI PRONTI E INIZIO ESECUZIONE mov current,7 ;il primo processo è aggiornato a 0 mov al,proc_total ;numero processi caricati mov ready_cnt,al ;numero processi pronti ready_next=ready_cnt cmp al,8 jb cont_22 ;oppure 0 per ready_cnt = 8 xor al,al cont_22: xor ah,ah mov ready_next,ax ;pointer al prossimo spazio libero nel buffer mov null_state,1 ;processo nullo jmp busy_loop$ ;inizio esecuzione CSEG ENDS END start INTERFACCIA RUN-TIME È stato scritto in assembly un modulo di collegamento verso MTRUN di nome MTUTL che non è direttamente eseguibile, ma una volta assemblato va collegato alle applicazioni che Laboratorio Quarta due um 35 di 42 a loro volta contengono richiami alle funzioni in esso definite. Tutti i richiami esterni sono indirizzati verso il kernel di MTRUN via interruzione S/W INT 62H, a eccezione delle funzioni showstring e clearscreen che sono eseguite in MTUTL. I parametri necessari sono passati tramite registri. File MTUTL.ASM vidseg equ 0B800h MTUTL SEGMENT ‘CODE’ assume cs:mtutl signal proc far public signal mov ah,01h int 62h ret 0 signal endp waitsem proc far public waitsem mov ah,02h int 62h ret 0 waitsem endp waittime proc far public waittime push bp mov bp,sp mov ax,[bp]+6 mov cx,91 mul cx mov cx,5 div cx mov dx,ax mov ah,03h int 62h pop bp ret 2 waittime endp gettime proc far public gettime push bp mov bp,sp mov di,[bp]+6 mov es,[bp]+8 mov ah,04h int 62h pop bp ret 6 gettime endp sendmessage proc far public sendmessage push bp mov bp,sp mov si,[bp]+ mov es,[bp]+8 mov ax,[bp]+ Laboratorio Quarta due ;inizio RAM video ;funzione semaforo SIGNAL in MTRUN ;codice per funzione SIGNAL ;chiamata a MTRUN ;ritorno al main ;funzione semaforo WAIT_SEM in MTRUN ;codice per funzione WAIT_SEM ;funzione di attesa per intervallo di tempo ;in MTRUN ;entrata standard ;tempo da stack in secondi ;18.2 tick/sec. approssimato da AX*91/5 ;AX*CX risultato in DX:AX ;DX:AX/CX risultato in AX ;codice per funzione WAIT_TIME ;funzione di lettura ora sistema in MTRUN ;entrata standard ;ES:DI punta alla stringa destinazione, in [bp]+10 ;è contenuta la lunghezza massima della stringa ;codice per funzione GET_TIME in MTRUN ;funzione scittura messaggio in mailbox ;ES:SI punta alla stringa d’ingresso ;numero mailbox [1..n] um 36 di 42 dec ax mov ah,05h int 62h pop bp ret 6 sendmessage endp recvmessage proc far public recvmessage push bp mov bp,sp mov di,[bp]+6 mov es,[bp]+8 mov ax,[bp]+10 dec ax mov ah,06h int 62h pop bp ret 6 recvmessage endp readstring proc far public readstring push bp mov bp,sp mov di,[bp]+6 mov es,[bp]+8 mov ah,07h int 62h pop bp ret 6 readstring endp showstring proc far public showstring cli push bp mov bp,sp mov si,[bp]+6 mov ds,[bp]+8 mov bx,[bp]+12 mov di,[bp]+14 mov cx,[bp]+16 mov ax,160 dec cl mul cl shl di,1 add di,ax mov ax,vidseg mov es,ax cld lodsb mov cl,al mov al,bl loop_1: cmp cl,0 je cont_1 Laboratorio Quarta due ;mailbox [0..n-1] per MTRUN ;codice per PUT_MAILBOX ;funzione di lettura messaggio da mailbox ;ES:DI punta alla stringa di destinazione ;numero mailbox [1..n] ;mailbox [0..n-1] per MTRUN ;codice per GET_MAILBOX ;funzione lettura stringa dal buffer tastiera ;ES:DI punta alla stringa di destinazione ;in [bp]+10=lunghezza massima della stringa ;codice per READ_STRING ;presentazione stringa su schermo, con posizione ;e modo di apparizione per evitare interruzioni ;DS:DI punta alla stringa in ingresso ;in [bp]+10=lunghezza massima stringa ;BL=modo di rappresentazione ;DI=colonna su video ;CL=riga su video ;calcolo posizione assoluta di destinazione in ;RAM video=(riga-1)*160+colonna*2 ;DI*2 ;DI=posizione relativa a inizio video ;ES=vidseg via AX ;ES:DI=destinazione RAM video ;spostamento per indirizzi crescenti ;lettura lunghezza stringa ;CL=lunghezza stringa ;AL=modo di rappresentazione um 37 di 42 movsb stosb dec cl jmp loop_1 cont_1: sti pop bp ret 12 showstring endp clearscreen proc far public clearscreen cli mov ax,vidseg mov es,ax xor di,di cld mov ax,0700h mov cx,2000 rep stosw sti ret 0 clearscreen endp MTUTL ENDS END ;un byte da stringa ingresso ;modo presentazione da AL ;spostamento byte successivo ;ripristino interruzioni ;funzione di cancellazione video ;vidseg=inizio area RAM video ;ES=vidseg ;ES:DI punta a inizio RAM video ;07=presentazione normale, 00=carattere nullo ;80 colonne *25 righe =2000 parole in area video Le applicazioni scritte in Pascal fanno uso di un modulo standard di definizione di funzioni e procedure esterne, è ricopiato all’interno delle applicazioni con l’istruzione $ I. In Pascal le chiamate al semaforo si chiamano Signal e WaitSem, in esse non è necessario passare alcun parametro. La chiamata alla funzione di attesa di tempo avviene per mezzo della procedura WaitTime, che accetta un parametro intero t indicante il tempo richiesto di attesa in secondi. Dato che la routine di servizio accetta solo valori di attesa espressi in tick, spetta al modulo d’interfaccia fare la conversione con tick=t(s)*18.2. L’ora gestita da MTRUN è letta in una variabile stringa per mezzo della procedura GetTime. Per le operazioni su mailbox si fa uso di funzioni che restituiscono un valore che indica la riuscita o meno dell’operazione. La scrittura avviene con SendMessage, i cui parametri in ingresso sono il numero della mailbox prescelta e la stringa da inviare; la lettura avviene con la funzione RevMessage, i cui parametri in ingresso sono il numero della mailbox e la stringa di uscita. L’interfaccia da Pascal è simile a quella richiesta dal kernel, con una differenza: le mailbox sono numerate da uno a mbx-num. La routine MTUTL d’interfaccia sottrae uno dal numero della mailbox prima di eseguire il richiamo ai moduli di MTRUN. La lettura da tastiera usa la funzione ReadString, il parametro è una stringa di lunghezza Laboratorio Quarta due um 38 di 42 variabile nella quale è copiato l’ingresso da tastiera. In MTUTL sono anche definite due procedure ausiliarie, che non sono gestite da MTRUN ma per comodità sono realizzate in assembly. ClearScreen serve a cancellare il contenuto dello schermo azzerando l’area di memoria RAM relativa a esso. ShowString copia il contenuto di una stringa in una locazione dello schermo identificata da riga e colonna, il parametro pres è l’attributo del carattere. Definizioni standard per le funzioni di sistema di MTRUN. Il file MTDEF.PAS va richiamato all’interno di un PAS tramite l’istruzione seguente. {$I MTDEF.PAS} File MTDEF.PAS /*Definizioni standard per le funzioni di sistema di MTRUN*/ TYPE mailbox = 1..4; message = STRING[80]; PROCEDURE Signal; external; PROCEDURE Waitsem; external; PROCEDURE WaitTime(t:INTEGER); external; PROCEDURE GetTime(var t:STRING); external; FUNCTION SendMessage(mbx:MAILBOX;var m1:MESSAGE): WORD; external; FUNCTION RecvMessage(mbx:MAILBOX;var m2:MESSAGE): WORD; external; FUNCTION ReadString(var s:STRING) : WORD; external; PROCEDURE ShowString(lin,col,pres: WORD;var s:STRING); external; PROCEDURE ClearScreen; external; Compilazione e collegamento Le applicazioni scritte per essere eseguite sotto MTRUN sono compilate normalmente e collegate con le procedure esterne contenute in MTUTL con il comando seguente. c:\>link applicazione + MTUTL.OBJ I moduli risultanti, di tipo EXE, sono eseguiti solo nell’ambito di MTRUN dato che le funzioni run time in esso definite non sono presenti in MS-DOS. Prova dell’applicazione La prima e più importante prova riguarda il caricamento di applicazioni differenti e la loro esecuzione in parallelo. Si può per questo fare uso di semplici loop che proseguono all’infinito. Per esempio, p1, p2, p3. File P1.PAS program p1; {$I mtdef.pas} begin Clearscreen; while true do; end. Laboratorio Quarta due um 39 di 42 Le applicazioni sono richiamate con il comando seguente. Nella riga in alto che indica lo stato del sistema si può osservare il continuo avvicendarsi dei processi, anche l’orologio di sistema prosegue costantemente la sua attività. L’applicazione Wait2 serve per provare la funzione WaitTime di attesa di un intervallo di tempo, 2 secondi e le funzioni di presentazione su schermo, consiste in un ciclo che si ripete all’infinito. Ogni due secondi di attesa è presentata sullo schermo una stringa contenente l’ora corrente di sistema e quella alla quale è avvenuto il richiamo immediatamente precedente. File WAIT2.PAS program wait2; {$I mtdef.pas} const text_1=‘esecuzione WAIT2: ‘; var out_str:LSTRING (40);newtime:STRING (8);oldtime:STRING (8); begin Clearscreen; oldtime:=‘00:00:00’; repeat waittime(2);gettime(newtime);out_str:=text_1; concat (out_str,newtime);concat (out_str,’ (‘); concat (out_str,oldtime);concat (out_str,’ )’); showstring (6,10,7,out_str);oldtime:=newtime; until false; end. Le applicazioni Signal1 e SemWait1 servono per provare le funzioni relative al semaforo. La prima applicazione invia con Signal segnali a intervalli regolari, indicati in WaitTime e la seconda attende questi segnali con WaitSem. Se i processi di tipo Signal1 sono in numero superiore a quelli di tipo SemWait1 si osserverà un aumento continuo del valore di semaphore. Se sono i processi di tipo SemWait1 a essere in numero superiore si noterà che il valore di semaphore resta sempre uguale a zero e solo un processo per volta prosegue la sua esecuzione. File SIGNAL1.PAS program signal1;{$i mtdef.pas}const text_1 = ‘invio SIGNAL: ‘; var out_str: lstring(40);loctime: string(8); begin clearscreen; repeat signal;gettime(loctime);out_str:= text_1; concat(out_str,loctime);showstring(5,10,7,out_str); waittime(5); until false; Laboratorio Quarta due um 40 di 42 end. File SEMWAIT1.PAS program semwait1; {$i mtdef.pas} const text_1 = ‘semwait1 ricezione signal: ‘; var out_str: lstring(80);loctime : string(8); begin repeat waitsem;gettime(loctime); out_str:= text_1;concat(out_str,loctime); showstring(10,10,7,out_str); until false; end. Per la prova delle mailbox si fa uso di MBXIN e MBXOUT. MBXIN accetta una stringa da tastiera e la mette nella mailbox numero uno. MBXOUT controlla periodicamente lo stato della mailbox e, se vi sono messaggi, li preleva e ne mostra il contenuto sullo schermo. File MBXIN.PAS program mbxin; (*programma di prova di: - lettura stringa da tastiera; - invio messaggi via mailbox; - presentazione su schermo.*) {$i mtdef.pas} const text_1 = ‘Spedizione regolare in mbx1 ‘; text_2 = ‘Errore spedizione in mbx1 ‘; text_3 = ‘Spazio insufficiente in mbx1 ‘; text_4 = ‘nessun messagio da inviare ‘; var msg1: message;out_str: lstring(40);result_1: word; begin clearscreen; repeat waittime(2);result_1:= readstring(msg1); showstring(4,0,7,msg1); if (msg1.len>0) then begin result_1:= sendmessage(1,msg1); case result_1 of 0: out_str:= text_1; 1: out_str:= text_3; otherwise out_str:= text_2; end; end else out_str:= text_4; showstring(4,40,112,out_str); until false; end. File MBXOUT.PAS program mbxout; {$i mtdef.pas} const text_1 = ‘Messaggio in uscita = Laboratorio Quarta due ‘; um 41 di 42 var begin text_2 = ‘Ricezione regolare in mbx1 ‘; text_3 = ‘Errore ricezione in mbx1 ‘; text_4 = ‘nessun messagio in mbx1 ‘; msg1: message;out_str: lstring(40);result_1 : word; out_str:= text_1; showstring(10,0,7,out_str); repeat result_1:= recvmessage(1,msg1); showstring(10,26,112,msg1); case result_1 of 0: out_str:= text_2; 2: out_str:= text_4; otherwise out_str:= text_3; end; showstring(12,0,112,out_str);waittime(5); until false; end. Laboratorio Quarta due um 42 di 42 UBERTINI MASSIMO http://www.ubertini.it [email protected] Dip. Informatica Industriale I.T.I.S. "Giacomo Fauser" Via Ricci, 14 28100 Novara Italy tel. +39 0321482411 fax +39 0321482444 http://www.fauser.edu [email protected]
© Copyright 2024 ExpyDoc