SISTEMES OPERATIUS (pla 98)
Enginyeria Tècnica Informàtica de Sistemes
Enginyeria Tècnica Informàtica de Gestió
Escola Tècnica Superior d'Enginyeria
Enunciat de la Pràctica
Curs 2001-2002
Nucli d’un Sistema Operatiu
M. Àngels Moncusí i Mercadé
Carles Aliagas i Castell
Índex
1. Introducció *
1.2. Nucli d'un sistema operatiu *
2.2. Crides al sistema per a la manipulació de processos *
2.3. Rutines de suport internes al nucli *
3.2. Crides al sistema pel sincronisme de processos *
5.2. Laboratori 1: Disseny d'estructures i implementació de CrearProcés *
5.3. Laboratori 2 i 3: Implementació de l'algorisme de planificació. *
5.4. Laboratori 4: Implementació del TRAP. *
5.5. Laboratori 5: Implementació del sincronisme entre processos *
5.6. Laboratori 6: Entrada i Sortida. *
5.7. Laboratori 7: Joc de proves definitiu. *
5.8. Documentació. *
7. Lliurament de la pràctica *
8. ANNEX 1: Rutines de suport. *
9.2. Fitxer cues.c *
Donada la complexitat d'un sistema operatiu, resulta convenient i desitjable, separar d'una manera clara i precisa la seva implementació en capes ben estructurades. De manera que cada capa implementi un subconjunt de funcions d'acord amb la seves característiques temporals i el seu nivell d'abstracció. Un nivell superior es recolza en el seu nivell immediatament inferior per tal d'implementar noves funcions i amagar els detalls d'implementació de les del nivell inferior. De la mateixa manera cada nivell ofereix els seus serveis al seu nivell superior. Això permetrà que les decisions o canvis que es realitzin en un nivell no afectin als altres nivells.
Per tal de permetre aquesta estructura en capes d'un sistema operatiu cada capa ha de tenir definida la seva interfície amb les altres capes.
En el cas concret de la pràctica, únicament hi haurà dos nivells, un pel sistema operatiu i un pels processos d'usuari. El nivell inferior o nucli treballarà directament amb el hardware de la màquina i oferirà al nivell superior una sèrie de funcions. El segon nivell usarà les operacions que proporciona el nivell nucli i oferirà als processos d'usuari una sèrie de crides al sistema. D'aquesta manera, quedarà completament separat l'espai de sistema de l'espai d'usuari.
Entre els nivells es creen interfícies d'accés. Aquestes interfícies permeten no només estructurar la implementació del Sistema Operatiu, sinó que prohibeixen la compartició de dades i funcions entre els nivells. Un nivell només pot fer crides a les rutines del nivell immediatament inferior que estan establertes en la interfície del nivell inferior. Això implica que una crida al sistema no podrà invocar a una altra crida al sistema del mateix nivell. Entre el nivell d'usuari i el nivell de sistema s'implementarà un mecanisme que impedirà que els processos d'usuari puguin accedir a variables i estructures que pertanyen intrínsecament al nivell de sistema. Aquests mecanismes, com es veurà més endavant, seran les crides a funcions mitjançant traps.
Per tant, ha d'oferir una interfície ben definida per a la utilització fàcil i eficient dels recursos hardware de la màquina. Així doncs, el nucli proporcionarà una sèrie de funcions que podran ser usades pel nivell immediatament superior, sense que aquest nivell s'hagi de preocupar de com estan implementades aquestes funcions.
L'objectiu de la pràctica és realitzar una part del nucli d'un sistema operatiu, en concret la part de gestió de processos, una part de sincronisme, i l'accés a dos dispositius físics. No se implementarà la part de gestió de memòria ni el sistema de fitxers.
El nucli identifica un procés pel seu PCB (Proces Control Block), que és una estructura de dades interna al nucli que conté tota la informació necessària per l’execució correcta del procés. Donat que no s’implementarà la part de gestió de memòria, no es podrà usar memòria dinàmica. Per tant el nucli tindrà un número finit i limitat de PCBs, cosa que implica que el número de processos que poden estar en un moment determinat actius en el sistema està limitat i és igual al nombre de PCBs existents.
La informació que el nucli necessita conèixer d’un procés pot ser la següent:
Les crides al sistema podran provocar canvis en l'estat propi del procés i en l’estat d'altres processos.
Un procés pot finalitzar per que es destrueix, o el destrueixen. Això provocarà l'eliminació del seu entorn i l'alliberació del seu PCB, de la seva zona de pila i de tots els recursos que tingui assignats.
Donat que en el nucli hi haurà diversos processos que volen executar-se, s’ha de decidir en quin ordre s’executaran els processos. La rutina que ho decideix és el planificador a curt termini. El planificador de curt termini que volem implementar és el d'una cua multinivell. Hi haurà dues cues que seguiran una política Round Robin amb prioritats estàtiques i apropiacions. A la cua més prioritària el quantum serà de quatre tics de rellotge, mentre que a la segona cua serà de vuit tics. Un procés de la primera cua que consumeixi dues vegades consecutives tot el quantum assignat, es degradarà a la segona cua. Mentre que un procés de la segona cua que consumeixi menys del quantum que li toca es promocionarà a la primera cua. Sempre que un procés entri a la cua de Ready des d'un estat diferent de RUN, ja sigui per que es nou, ja sigui que ve de l'estat de bloquejat, anirà a la primera cua.
Es reservarà la prioritat més baixa (no dinàmica) pel procés nul, que sols ha d'aconseguir la CPU quan no hi hagi cap més procés, i serà desbancat de la CPU quan apareix un procés a la cua de RDY. El procés nul no farà cap càlcul, però mostrarà per pantalla quan s'està executant.
En el nostre cas, un procés pot ser desbancat de la CPU i per tant provocar un canvi de context, pels següents motius:
Per tal de poder implementar el nucli, necessitarem una sèrie d'estructures de dades.
Pels processos
Per a les cues (Veure: Annex 2)
A continuació veurem un exemple de com s'usen les cues per encuar/inserir PCBs, encara que es poden usar per encuar qualsevol estructura de dades on el primer camp és un ítem.
InicialitzarCua ( ready )
Inserir
( ready, element, 4 )
Observació:
Per a un PCB d’un procés que està a la cua de RDY, els camps ordre i prioritat tenen el mateix valor. És a dir:
pPCB->Fil.ordre == pPCB->Prioritat
Les crides al sistema manipulen les estructures de dades internes del nucli, per tal de realitzar l’acció o accions necessàries que s’indiquen. Al formar part de la interfície del programador, la sintaxi i significat de les crides no es pot modificar. És aquesta restricció la que dóna independència de la implementació que s'usi.
int CrearProces (void (*codi)(), uint Prioritat)
Crea un nou procés i el posa a la cua de RDY, provocant una apropiació en cas necessari. Els paràmetres indiquen, respectivament, l’adreça de la primera instrucció a executar del procés, i la prioritat.
Retorna:
L’identificador del procés (IdProces) que s’ha creat si tot ha funcionat de forma correcta.
-1 si no s’ha pogut crear per falta de PCBs lliures.
-5 si s’ha produït qualsevol altre error.
int DestruirProces(uint IdProces)
Destrueix un procés, eliminant el seu context i alliberant tots els recursos que té assignats.
Si el procés està bloquejat el treu de la cua corresponent realitzant les accions compensatòries que es necessitin. Si el procés no existeix retorna error.
Retorna:
0 si tot ha funcionat de forma correcta.
-1 si el procés no existeix.
-2 si s'intenta destruir el procés nul.
uint QuiSoc(void)
Retorna l’identificador del procés (IdProces) que el crida.
void DormirProces (long NTics)
Atura el procés que l’executa durant el número de interrupcions de rellotge (NTics) que sol·licita. S’ha de tenir en compte que l’execució d’aquesta rutina comportarà un canvi de context immediat.
void multiplexar(void)
Rutina que tracta la interrupció de rellotge. Ha de decidir quan ha de realitzar-se el canvi de context per finalització del quantum, quan hi ha d'haver una degradació entre les diferents cues i quan s’ha de despertar a un procés aturat mitjançant la crida al DormirProces.
struct TipusPCB * scheduler(void)
Rutina que escull el procés de les cues de ready que passarà a executar-se.
void dispatcher(struct TipusPCB *proces)
Rutina que posa un procés en estat de run.
Utilitzarem semàfors n-àris per tal de realitzar el sincronisme entre diferents processos.
Per tal de poder implementar les rutines de sincronisme,
necessitarem disposar de la estructura TipusSemafor definida de la següent
manera:
Aquesta estructura disposa d'una cua, en aquest cas, però, el control de la cua ha de ser FIFO ja que els semàfors han de garantir la condició d'espera limitada tal com es va veure a l'assignatura d'ISO.
Al igual que els PCBs, en la inicialització del nucli, s’haurà de reservar memòria per tal de contenir el màxim nombre de semàfors que es vulguin utilitzar.
Inicialitza un semàfor amb l’identificador del semàfor i el valor que se li passa com a paràmetre. Els identificadors del semàfor sol poden ser inicialitzats una única vegada. Es dóna l’identificador per a facilitar la compartició entre processos d’usuari.
Retorna:
0 si tot ha funcionat de forma correcta.
-1 si no s’ha pogut crear per falta de semàfors lliures.
-2 si el semàfor ja estava inicialitzat.
int WaitS(uint IdSemafor)
Si el comptador del semàfor val 0, el procés es queda bloquejat en una cua associada al semàfor produint un canvi de context immediat, en cas contrari decrementa en 1 el valor del comptador associat al semàfor IdSemafor i retorna el control al procés.
Retorna:
0 si tot ha funcionat de forma correcta.
-1 si el semàfor sol·licitat no existeix
o no està inicialitzat.
int SignalS(uint IdSemafor)
Si la cua associada a aquest semàfor està buida, incrementa en 1 el valor del comptador associat al semàfor que se li passa com a paràmetre, altrament desbloqueja al primer procés que hi està encuat.
Retorna:
0 si tot ha funcionat de forma correcta.
-1 si el semàfor sol·licitat no existeix.
int ElimSem (uint IdSemafor)
Allibera el semàfor l'identificador del qual passem per paràmetre. No es pot eliminar un semàfor que tingui processos bloquejats a la seva cua.
Retorna:
0 si tot ha funcionat de forma correcta.
-1 si el semàfor sol·licitat no existeix .
-2 si el semàfor sol·licitat té processos bloquejats en la cua associada al semàfor.
En el nostre nucli es vol usar dos dispositius d'entrada i sortida. Aquests dos dispositius seran compartibles. Un és el teclat i l'altre la pantalla. Tots els processos, en el moment de la seva creació tindran assignats i oberts aquests dos dispositius compartibles com a entrada i sortida estàndard.
Per el dispositiu teclat es demana que s'implementi "buffering", es a dir, quan l'usuari premi tecles i no hi hagi cap procés llegint de teclat, aquestes es guarden en un buffer assignat al dispositiu. El proper procés que faci una lectura de teclat se li passaran les tecles emmagatzemades en aquest buffer, sense quedar-se bloquejat, si hi ha suficients tecles.
-1 si s'ha produït algun error.
-1 si s'ha produït algun error.
En aquesta pràctica, tal com s'ha dit anteriorment, s'ha d'implementar el nivell més intern d'un sistema operatiu. A més hi haurà el nivell d'usuari, on hi haurà els processos de prova, els quals accediran al sistema mitjançant crides al sistema, implementades a través d’un trap. Tingueu en compte que no es permès invocar a una crida al sistema dins d'una altra crida al sistema. Aquests processos realitzaran un joc de proves adient per tal de comprovar que tot funciona correctament.
Es recomana desenvolupar el nucli en diverses fases, de manera que no es començarà una nova fase si no s'està segur que l'anterior funciona correctament, doncs s’hi fonamentaran. Això permet anar desenvolupant el joc de proves i la documentació conforme es va evolucionant en la implementació.
S'ha d'anar en compte de separar clarament el que correspon al sistema, i el que correspon als processos que es realitzen per tal de provar la implementació. Per exemple, un procés d'usuari mai accedirà a variables del nucli.
El desenvolupament de la pràctica s'anirà realitzant en les diferents sessions de laboratori.
Per tal de realitzar la depuració del codi es recomana l’ús del Turbo Debugger, que permet seguir la evolució de la pila dels processos d’una manera senzilla i clara.
En aquest laboratori s’haurà de:En tot cas, l’Identificador del procés no ha de tenir res a veure amb la posició que ocupa el PCB del procés dins del vector, ja que aquests són reutilitzables, mentre que els Identificadors no s’haurien de reutilitzar de forma immediata a l’expiració d’un procés.
En el moment de la creació d’un procés se li ha de assignar l'adreça de memòria on es troba el codi que ha d'executar el procés en qüestió (en C la adreça del codi d'una rutina és equivalent al nom de la rutina) i una zona de memòria que el procés usarà com a pila.
Per tal de simplificar al màxim les estructures del procés, en lloc de tenir una zona de dades globals, es pot usar únicament variables locals al procediment definit, que el propi C ens deixarà en la pila del procés. En el moment d'iniciar el procés s'ha de tenir en compte aquesta zona i deixar el lloc adient a la pila.
En el moment de la creació del procés se li haurà de donar un entorn d’execució i encuar el PCB del procés a la cua de RDY. La creació de l’entorn consistirà en donar una pila i posar-li uns valors inicials (context del procés), per tal que en el moment de treure el procés de ready i iniciar-lo, es pugui fer restaurant el context del procés que hagi escollit el planificador. Els valors imprescindibles són: La paraula d’estat (s’ha d’anar en compte en deixar les interrupcions habilitades), els registres CS i IP, els registres SS i SP, el registre DS i l’enllaç dinàmic si l’useu.
La rutina CrearProces de moment, únicament, encuarà el nou procés a la cua de ready, sense tenir en compte possibles apropiacions.
Inicialment es pot fer que els processos no acabin, es a dir, executin un bucle infinit.
Restaurar: Aquesta rutina és la complementària de l'anterior, i és l' última que es cridarà dintre de qualsevol RSI escrita en "C". La seva funció és treure de la pila els registres guardats per la rutina Salvar, eliminant les possibles variables locals i retornant de la RSI amb IRET.
Restaurar el procés que es vol que s’executi en primer lloc. Quan sigui necessari la rutina multiplexar activarà el planificador i es podrà produir un canvi de context.
Com en aquestes fases s’està treballant a molt baix nivell (pe., tocant la pila) succeirà freqüentment que la pràctica es descontrolarà, podent deixar penjat el sistema i havent de reengegar la màquina.
Si voleu, heu de tenir en compte, que mentre s'està executant la rutina trap, poden arribar altres interrupcions (per exemple la del rellotge) que provoquin un canvi de context en el procés de RUN. Aquest fet obliga a tenir varis contexts (un per interrupció que es pugui provocar) guardats en l'estructura del PCB. En el moment de restaurar el context, s'haurà de restaurar en ordre invers a com s'ha salvat.
També podeu implementar el trap de manera que no permeteu que us arribin interrupcions mentre esteu realitzant les crides al sistema. Si ho feu així, tant sols es podrà produir un canvi de context quan ho sol·liciti/imposi la crida al sistema que s'executa.
Per tal de provar que funciona, un procés haurà de crear dos processos de manera consecutiva. El primer procés ha de ser més prioritari que el procés creador, i el segon procés més prioritari que el primer. Si la apropiació està ben implementada, veurem que el primer procés s'apropiarà immediatament de la CPU no permeten la creació del segon procés.
A partir d'aquest moment totes les crides al sistema que aneu implementant ja hauran de passar sempre a través del trap.
Si un procés de prova no executa un bucle infinit llavors quan finalitzi el seu codi sempre s'haurà de cridar a la rutina DestruirProces, altrament, el procés (que implementem com una subrutina) retornarà. Al usar una pila pròpia aquest procés no sabria on retornar, dependria de que hagués en aquell moment al capdavall de la pila.
Per tal de comprovar el seu bon funcionament, heu de fer una sèrie de processos que creïn altres processos els quals finalitzin la seva execució cridant a la rutina de DestruirProces. S’ha de tenir en compte que aquesta rutina pot destruir el procés que l’executa o pot destruir qualsevol altre procés que es passi com a paràmetre i no sigui el procés nul.
Cal fer un joc de proves específic, doncs el seu correcte funcionament és vital per a les següents fases. Els diferents processos podran ‘compartir’ un semàfor, ja que el seu identificador és assignat pel programador, el qual, a la vegada, haurà de ser molt curós i assegurar-se que els processos que usen un semàfor són els que l'han d'usar.
No us oblideu de comprovar:
En el moment de carregar el nostre nucli, es crearan dos processos: Un procés nul, amb prioritat 0 i un procés shell amb prioritat màxima: Aquest procés serà la base de tot el nostre joc de proves. Tots els processos que especifiquem han d'estar en un fitxer procs.c
En tot moment s'ha de veure la informació (identificador procés, prioritat i quantum consumit) dels processos (actius) que hi ha en les diferents cues. Penseu que pel estat de ready hi ha dues possibles cues.
(Es poden crear unes rutines que ens facin l'actualització en pantalla de l'evolució dels processos)
El procés nul executarà un bucle infinit que escriurà per pantalla un "molinet" de manera continua.
El procés shell executa la crida Quisoc escrivint per pantalla el seu codi de retorn. A continuació entrarà en un bucle infinit esperant comandes.
Les comandes mínimes a implementar són:
R y => executar la prova número y.
K => mata tots els processos actius excepte el nul i la shell.
Així doncs serà aquest procés inicial (la shell) el que permetrà anar executant diferents processos que comprovaran el correcte funcionament de la pràctica.
Processos de prova
1. Comprovació del Round Robin.
Executar el mateix procés 6
vegades amb la mateixa prioritat i un comportament orientat a CPU. Heu
de comprovar que els processos vagin realitzant els canvis de les cues
de ready conforme van consumint el quantum.
2. Comprovació quantum
Executar tres processos amb la mateixa
prioritat i amb el següent comportament: Un orientat a E/S, un orientat
a CPU i un de mixt. S'ha de comprovar que el procés mixt va canviant
de cua de ready.
3. Comprovació de les prioritats
Executar tres processos amb diferent
prioritat, observar que sempre s'executa el procés de major prioritat.
4. Comprovació de DormirProces
Crear 3 processos idèntics
amb la mateixa prioritat i en aquest ordre: un procés que es dormi
15 segons, un altre que es dorm 20 segons i un altre que es dormi 10 segons.
Abans de dormir-se ho ha de notificar per pantalla i tant aviat com es
desperti també.
5. Comprovació de prioritats
i de l'apropiació.
Procés que crearà tres
processos. Primer un d'una prioritat baixa (baix), l'altre amb prioritat
alta (alt) i el tercer amb una prioritat intermèdia (mig).
A continuació escriurà un missatge de finalització
i s'autodestruirà. Desprès de la crida DestruirProces,
es comprovarà el codi de retorn i entrarà en un bucle infinit
escrivint lletres per pantalla.
El procés alt escriurà un conjunt de lletres per pantalla i es bloquejarà durant 10 segons. Quan es desperti destruirà els processos baix i mig, escrivint per pantalla el codi de retorn.
El procés mig escriurà lletres per pantalla en un bucle infinit
El procés baix escriurà un conjunt de lletres a la pantalla i destruirà el procés mig, escrivint per pantalla el codi de retorn de la crida DestruirProces.
6. Comprovació del comportament
dels semàfors
Crear cinc processos idèntics
amb la mateixa prioritat que escriguin diferents lletres per pantalla de
manera sincronitzada usant un únic semàfor, en cada moment,
sols un d'ells pot escriure a la pantalla.
7. Comprovació del comportament
del teclat
Crear tres processos que llegeixin
strings per teclat i els escriguin a la pantalla, juntament amb el nombre
de caràcters sol·licitats i els realment llegits.
8. Comprovació de la reutilització
dels PCB's
Engegar tots els processos creats
anteriorment al mateix temps, es a dir, crearem més processos que
el nombre de pcb's definits.
9. Implementeu el problema del filòsofs que pensen i mengen usant semàfors. Implementeu-lo de manera que estigui lliure de deadlocks (estancaments).
10. Altres comprovacions que creieu convenients per tal de fer un joc de proves més complert.
La documentació de la pràctica es pot anar realitzant conforme es vagi avançant en el desenvolupament, sobretot a partir de la realització de la 3ª fase, de manera que al final sols quedi ajuntar-ho tot en un únic document.#include
"estructures.h"
void main(void) { inicialitzacions(); CanviarVectInt (0x8,Multiplexar); ; CanviarVectInt (0x40,trap); Crear_proces_nul (); Crear_proces_init (); /* al nivell de l’usuari: serà el pare de la resta */ Dispatcher(); Restaurar(proces_run->ss_sp); } |
|
int
CrearProces_K(context c, void (*codi)(),uint Prioritat)
{ /* codi necessari per tal de crear un procés. Context és una estructura de dades creada per tal de facilitar l'accés als paràmetres de les rutines: struct context { int es,ds,ax,bx,cx,dx,di,si,bp; /*valors dels registres del processador*/ long @_retorn_del_trap_al_codi_asm; int flag; long @_retorn_crida_sistema_al_proces_usuari; } */ } .... |
void
trap(void)
{ /* prun apunta al procés que esta en RUN */ prun->ss_sp=salvar(); /* ss_sp apunta al top de la pila del procés de RUN */ desinhibir(0x200); /* RSI: interrupcions desactivades => cal fer I=1 */ /* així el codi de les crides al sistema serà interrompible */ switch (rutina_retorna_ax) /*rutina és un tipus enumerat, el valor del qual s'ha de passar a través de la pila del procés (associada a AX), on hi ha quina crida al sistema que s'ha realitzat */ { case crear: CrearProces_K(); break; ... default: break; } inhibir(); /* no ens han d’interrompre a mig restaurar ! */ restaurar(prun->ss_sp) ; } |
SYSCALLS
SEGMENT BYTE PUBLIC 'CODE'
Assume cs:SYSCALLS Public _CrearProces _CrearProces proc far mov ax, CodiCrearProces ; indicarà quina crida al sistema es vol realitzar int 40h ret _CrearProces endp ..... SYSCALLS ENDS END |
#include
"syscalls.h"
#define NORMAL 0x7 void Init(void) /* Proces de sistema que executarà el nostre joc de proves*/ { CrearProces(procesA,3,1); ... for (;;); } void Nul(void) /* Proces de sistema que s'executarà quan no hi hagi ningú més*/ { int i; char roda[4]={|,/,-,\}; for (;;) { for(i=0;i<4;i++) { EscriureCar(quisoc(),0,roda[i],NORMAL); retard(10); } } } void procesA(void) { int identificador; /* variables locals */ int i; i=0; identificador=CrearProces(procesB,3,1); for (;;) { EscriureCar(quisoc(),i,"A",NORMAL); retard(10); i++; if (i=80) i=0; } } |
Data límit de lliurament convocatòria extraordinària: 21 de gener del 2002
Data límit de lliurament primera convocatòria: 3 de juny del 2002
Data límit de lliurament segona
convocatòria: 2 de setembre del 2002
Una vegada lliurada tota la documentació es fixarà la data de l'entrevista amb tots els membres del grup.
Les pràctiques es podran passar a recollir
des de l'octubre al desembre del 2002.
Podreu usar les següents rutines
typedef unsigned int WORD; /* Natural de 16 bits */
typedef unsigned char BYTE; /* Natural de 8 bits */
typedef void * PTR; /* Punter genèric (far) */
PTR Ptr (WORD Segment, WORD Desplacament); /* Converteix el segment i desplaçament en un punter */
WORD Segment (PT0R Adreca); /* Retorna el segment del punter donat */
WORD Desplacament (PTR Adreca); /* Retorna el desplaçament del punter donat */
WORD Word (BYTE msb, BYTE lsb); /*Retorna un WORD amb part baixa i alta els donats*/
BYTE MSB (WORD Valor); /* Retorna el byte alt del word donat */
BYTE LSB (WORD Valor); /* Retorna el byte baix del word donat */
WORD Inhibir (void ) /* Inhibeix les interrupcions (posa IF=0), retornant el valor de la paraula d'estat (flags) abans d'inhibir. Per llegir els flags es útil la instrucció PUSHF. */
void Desinhibir (WORD Flags); /* Carrega el valor passat com a paràmetre com a nou valor de la paraula d'estat (flags). Per escriure els flags podem s'usarà la instrucció POPF. */
void EOI (void); /* Envia al controlador d'interrupcions (i8259) una comanda de final d'interrupció no específica (EOI). */
void (*CanviarVectInt (BYTE NumInt, void (*NovaRSI)() ))(); /* Assigna una nova rutina de servei d'interrupció a una de les posicions del vector d'interrupcions (V.I.), retornant l'adreça de l'anterior rutina de servei, per tal de restaurar-la més endavant. Quan s'estigui accedint al Vector d'Interrupcions les interrupcions han d'estar inhibides, deixant-les després en el mateix estat (inhibides o no) que quan es va cridar aquesta rutina. */
PTR BufferPantalla (void); /* Retorna l'adreça on està mapejada la pantalla. Emmagatzema internament aquesta informació per ser usada per les rutines posteriors. En cas que el mode de pantalla no sigui reconegut retorna un valor que indica que s'ha produït un error. */
void EscriureCar (BYTE fila, BYTE columna, char caracter, BYTE atribut); /* Escriu a pantalla el caràcter que se li passa, amb l'atribut indicat, a les coordenades especificades. La informació d'on es troba mapejada la pantalla l'obté de la variable que ha inicialitzat la rutina BufferPantalla(). */
void EscriureString (BYTE fila, BYTE columna, char * string, BYTE atribut); /* Escriu a pantalla la cadena de caràcters que se li passa, a partir de la posició donada, i amb el mateix atribut per tots els caràcters. La seva implementació utilitza instruccions de tractament de strings de l'i8086 (LODSB i STOSW). La informació d'on es troba mapejada la pantalla l'obté de la variable que ha inicialitzat la rutina BufferPantalla(). */
void EsborrarPantalla (BYTE atribut); /* Omple tota la pantalla de caràcters blancs (espai, codi ASCII 32) amb l'atribut que se li indica. També fa servir instruccions de tractament de strings (concretament, REP STOSW). La informació d'on es troba mapejada la pantalla l'obté de la variable que ha inicialitzat la rutina BufferPantalla().*/
public _Ptrpublic _Segment
public _Desplacament
public _Word
public _MSB
public _LSB
public _BufferPantalla
public _EscriureCar
public _EscriureString
public _EsborrarPantalla
public _QuinCarAtr
public _Inhibir
public _Desinhibir
public _EOI
public _Salvar
public _Restaurar
public _CanviarVectInt
public _InhibirIRQ
public _DesinhibirIRQ
_DATA segment word public 'DATA'
SegmPant dw ?
_DATA ends
_BSS segment word public 'BSS'
_BSS ends
DGROUP group _DATA, _BSS
assume cs:EC_CODI, ds:DGROUP
EC_CODI segment byte public 'CODE'
_Ptr PROC FAR
PUSH BP
MOV BP,SP
MOV AX,[BP+8]
MOV DX,[BP+6]
POP BP
RET
_Ptr ENDP
_Segment PROC FAR
PUSH BP
MOV BP,SP
MOV AX,[BP+8]
POP BP
RET
_Segment ENDP
_Desplacament PROC FAR
PUSH BP
MOV BP,SP
MOV AX,[BP+6]
POP BP
RET
_Desplacament ENDP
_Word PROC FAR
PUSH BP
MOV BP,SP
MOV AL,[BP+8]
MOV AH,[BP+6]
POP BP
RET
_Word ENDP
_MSB PROC FAR
PUSH BP
MOV BP,SP
MOV AL,[BP+7]
POP BP
RET
_MSB ENDP
_LSB PROC FAR
PUSH BP
MOV BP,SP
MOV AL,[BP+6]
POP BP
RET
_LSB ENDP
_BufferPantalla PROC FAR
PUSH ES
XOR AX,AX
MOV ES,AX
MOV AL,ES:[0449h]
CMP AL,7
JE Monocrom
CMP AL,2
JE Color
CMP AL,3
JE Color
Error: MOV DX,0
JMP Sortida
Color: MOV DX,0B800h
JMP Sortida
Monocrom: MOV DX,0B000h
Sortida: MOV SegmPant,DX
XOR AX,AX
POP ES
RET
_BufferPantalla ENDP
_EscriureCar PROC FAR
PUSH BP
MOV BP,SP
PUSH ES
PUSH BX
PUSH AX
MOV ES,SegmPant
MOV AL,[BP+6]
MOV BX,80
MUL BL
MOV BL,[BP+8]
ADD BX,AX
SHL BX,1
MOV AL,[BP+10]
MOV AH,[BP+12]
MOV ES:[BX],AX
POP AX
POP BX
POP ES
POP BP
RET
_EscriureCar ENDP
_EscriureString PROC FAR
PUSH BP
MOV BP,SP
PUSH ES
PUSH DS
PUSH AX
PUSH BX
PUSH SI
PUSH DI
MOV ES,SegmPant
MOV AL,[BP+6]
MOV BL,80
MUL BL
ADD AL,[BP+8]
ADC AH,0
SHL AX,1
MOV DI,AX
LDS SI,[BP+10]
MOV AH,[BP+14]
CLD
BUCLE: LODSB
CMP AL,0
JE FiBucle
STOSW
JMP BUCLE
FiBucle: POP DI
POP SI
POP BX
POP AX
POP DS
POP ES
POP BP
RET
_EscriureString ENDP
_EOI PROC FAR
PUSH AX
MOV AL,20h
OUT 20h,AL
POP AX
RET
_EOI ENDP
_Inhibir PROC FAR
PUSHF
CLI
POP AX
RET
_Inhibir ENDP
_Desinhibir PROC FAR
PUSH BP
MOV BP,SP
PUSH AX
MOV AX,[BP+6]
PUSH AX
POPF
POP AX
POP BP
RET
_Desinhibir ENDP
_EsborrarPantalla PROC FAR
PUSH BP
MOV BP,SP
PUSH ES
PUSH AX
PUSH CX
PUSH DI
MOV ES,SegmPant
MOV DI,0
MOV AH,[BP+6]
MOV AL,' '
MOV CX,80*25
CLD
REP STOSW
POP DI
POP CX
POP AX
POP ES
POP BP
RET
_EsborrarPantalla ENDP
_QuinCarAtr PROC FAR
PUSH BP
MOV BP,SP
PUSH ES
PUSH BX
MOV ES,SegmPant
MOV AL,[BP+6]
MOV BX,80
MUL BL
MOV BL,[BP+8]
ADD BX,AX
SHL BX,1
MOV AX,ES:[BX]
POP BX
POP ES
POP BP
RET
_QuinCarAtr ENDP
_CanviarVectInt PROC FAR
PUSH BP
MOV BP,SP
PUSH ES
PUSH BX
XOR AX,AX
MOV ES,AX
MOV AL,[BP+6]
SHL AX,1
SHL AX,1
MOV BX,AX
MOV AX,[BP+8]
MOV DX,[BP+10]
PUSHF
CLI
XCHG AX,ES:[BX]
XCHG DX,ES:[BX+2]
POPF
POP BX
POP ES
POP BP
RET_CanviarVectInt ENDP
_Salvar PROC FAR
PUSH BX
MOV BX,SP
XCHG CX,SS:[BX+2]
XCHG DX,SS:[BX+4]
PUSH AX
PUSH DI
PUSH SI
PUSH ES
PUSH DS
MOV AX,DGROUP
MOV DS,AX
PUSH DX
PUSH CX
RET
_Salvar ENDP
_InhibirIRQ PROC FAR
PUSH BP
MOV BP,SP
PUSH AX
PUSH CX
MOV CL,[BP+6]
MOV AH,1
SHL AH,CL
PUSHF
CLI
IN AL,21h
OR AL,AH
OUT 21h,AL
POPF
POP CX
POP AX
POP BP
RET
_InhibirIRQ ENDP
_Restaurar PROC FAR
ADD SP,4
POP DS
POP ES
POP SI
POP DI
POP AX
POP BX
POP CX
POP DX
MOV SP,BP
POP BP
IRET
_Restaurar ENDP
_DesinhibirIRQ PROC FAR
PUSH BP
MOV BP,SP
PUSH AX
PUSH CX
MOV CL,[BP+6]
MOV AH,1
SHL AH,CL
NOT AH
PUSHF
CLI
IN AL,21h
AND AL,AH
OUT 21h,AL
POPF
POP CX
POP AX
POP BP
RET
_DesinhibirIRQ ENDP
EC_CODI ends
end
Per tal de estalviar-vos feina, us proporcionem el codi d’unes rutines per a realitzar la gestió de cues d’estructures de dades genèriques. El seu ús és, òbviament, opcional. També, si voleu, en podeu desenvolupar de noves si trobeu a faltar alguna funcionalitat. Finalment, coneguda l’estructura de les cues, òbviament podeu recórrer-les mitjançant punters en el vostre propi codi.
struct cua *(inicialitzar_cua (struct cua *c))
Inicialitza una estructura cua, fa que el punter al primer i últim ítem de la cua apuntin a l'estructura cua, o sigui, a cap element.
int buida(struct cua *c)
si la cua està buida retorna cert
struct item *(encuar (struct cua c, struct item elem))
posa al final de la cua l'ítem especificat
struct item * (inserir (struct cua *c, struct item * elem, int ordre)
insereix un ítem en una cua ordenada segons el valor del camp ordre
struct item *(primer (struct cua *c))
retorna un punter al primer ítem de la cua i el desencua. Si no n’hi ha cap retorna NIL.
struct item *(extirpar (struct cua *c, struct item *elem))
extreu de la cua l'ítem desitjat. Si no el troba retorna NIL
struct item *(extreure (struct cua *c, int ordre))
extreu el primer ítem de la cua amb el camp ordre desitjat. Si no n’hi ha cap retorna NIL
/* E.T.S.E / Departament d'Enginyeria Informàtica ETIS/ETIG */
/* Sistemes Operatius CUES: defines & externs */
#ifndef CUES-H
#define CUES-H
#define NIL 0
#ifdef MODUL_CUES
struct item
{ struct item *seguent;
struct item *anterior;
int ordre;
};
struct cua
{ struct item *primer;
struct item *ultim;
int ordre; /* no usat, sols per coherencia */
};
#else
extern struct cua *(inicialitzar_cua(struct cua *c));
extern int buida(struct cua *c);
extern struct item *(encuar (struct cua *c, struct item *i));
extern struct item *(inserir (struct cua *c, struct item *i, int ordre));
extern struct item *(primer (struct cua *c));
extern struct item *(extirpar (struct cua *c, struct item *e));
extern struct item *(extreure (struct cua *c, int ordre));
#endif
#endif
/* Sistemes Operatius CUES */
#define MODUL_CUES
#include "cues.h"
/* Funcions per a inicialitzar i mantenir cues doblement encadenades */
/* Els elements han de ser estructures de manera que els primers camps siguin: */
/* struct item *seguent, *anterior; */
/* int ordre; criteri d'ordenació (numero d'ordre) */
/* NOTA: NO es creen les instanciacions a memòria de les estructures */
/*--------------------------------------------------------------------------- */
/* Inicialitza una estructura cua, fa que el punter al primer i últim */
/* ítem de la cua apuntin a l'estructura cua, o sigui, a cap element. */
struct cua *(inicialitzar_cua (struct cua * c))
{ if (c!=NIL) { c->ultim = c->primer = (struct item *) c; }
return(c);
}
/*--------------------------------------------------------------------------- */
/* Funció que pregunta si la cua està buida, i si ho està retorna cert. */
int buida(struct cua * c)
{ return((c->primer == NIL) || (c->primer == (struct item *) c)); }
/*--------------------------------------------------------------------------- */
/* Procediment que insereix en una cua ordenada un un ítem en un ordre donat */
struct item * (inserir (struct cua * c, struct item * elem, int ordre))
{ struct item * ant, * post;
if (c==NIL) return (NIL); /* cua no creada */
ant= c->ultim;
if (ant==NIL || elem==NIL) return(NIL); /* cua no inic. o no elem */
elem->ordre= ordre;
if (ant == (struct item *) c) /* cua buida : serà primer i darrer */
{ elem->seguent= elem->anterior= (struct item *) c;
c->primer= c->ultim= elem;
return(elem);
}
/* recorrem la cua des de darrera, doncs encuem ! */
post= (struct item *) c;
while ( (ant != (struct item *) c) /* !principi de cua i prioritat menor */
&& (ant->ordre) < ordre) /* ordre(0) < ordre(n) */
{ post= ant; /* ordre = ? => a la cua: RoundRobin */
ant= ant->anterior;
}
/* ja sabem on li toca: fer-ho efectiu */
ant->seguent= elem; /* pel davant */
elem->anterior= ant;
elem->seguent= post; /* i pel darrera */
post->anterior= elem;
return(elem);
}
/*--------------------------------------------------------------------------- */
/* Procediment que fica al final de la cua l'ítem especificat. */
struct item * (encuar (struct cua * c, struct item * elem))
{ struct item * aux;
if (c==NIL) return (NIL); /* cua no creada */
aux= c->ultim;
if (aux==NIL || elem==NIL) return(NIL); /* cua no inic. o no elem */
elem->seguent= aux->seguent; /* al final de la cua */
elem->anterior= aux; /* recorda darrera qui vas */
(aux->seguent)->anterior= elem; /* == c->ultim= elem; */
aux->seguent= elem; /* el de davant el veu */
return(elem);
}
/*--------------------------------------------------------------------------- */
/* Funció que extreu de la cua l'ítem desitjat. Si no el troba o la cua */
/* no està inicialitzada retorna NIL. */
struct item *(extirpar (struct cua *c, struct item *elem))
{ struct item * post, * ant;
if (c==NIL) return (NIL); /* cua no creada */
post= c->primer;
if (post==NIL) return (NIL); /* cua no inicialitzada */
ant= (struct item *) c; /* localitzem la seva posició dins la cua */
while ((post!=(struct item *) c) && (post!=elem)) /* !fi de cua i !trobat*/
{ ant= post;
post= post->seguent;
}
if (post == (struct item *) c) return(NIL); /* no trobat */
ant->seguent= post->seguent; /* treure'l */
(post->seguent)->anterior= ant;
post->seguent= NIL; /* ara ja no es a la cua */
post->anterior= NIL;
return (post);
}
/*--------------------------------------------------------------------------- */
/* Funció que extreu de la cua ordenada un ítem amb el número d'ordre indicat */
/* Si no el troba o la cua no està inicialitzada retorna NIL. */
struct item *(extreure (struct cua *c, int ordre))
{ struct item * post, * ant;
if (c==NIL) return (NIL); /* cua no creada */
post= c->primer;
if (post==NIL) return (NIL); /* cua no inicialitzada */
ant= (struct item *) c; /* localitzem la seva posició dins la cua */
while ((post!=(struct item *) c)
&& (post->ordre > ordre)) /* !fi de cua i !trobat */
{ ant= post;
post= post->seguent;
}
if ((post == (struct item *) c) || (post->ordre != ordre))
return(NIL); /* no trobat ningú amb aquest número d'ordre */
ant->seguent= post->seguent; /* treure'l */
(post->seguent)->anterior= ant;
post->seguent= NIL; /* ara ja no es a la cua */
post->anterior= NIL;
return (post);
}
/*--------------------------------------------------------------------------- */
/* Funció que retorna un punter al primer ítem de la cua i el desencua. */
/* Si la cua no està inicialitzada o està buida retorna NIL. */
struct item *(primer (struct cua * c))
{ struct item * aux;
if (c==NIL) return (NIL); /* cua no creada */
aux= c->primer;
if (aux==NIL || aux==(struct item *)c) return(NIL); /*cua no inic o no elem*/
c->primer= aux->seguent;
(aux->seguent)->anterior= (struct item *) c;
aux->seguent= NIL; /* ara ja no es a la cua */
aux->anterior= NIL;
return(aux);
}