Attività di laboratorio 2

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]