Posted By: sayza (sayza) on 'CZprogram' Title: multitask source Date: Sun Feb 16 12:58:41 1997 Hi programatori, pred casom som robil s kamosom zadanie do skoly z predmetu TOS. Tvorba Operacnych Systemov. Mno, OS sme sice ziaden nespravili, ale zato sa mi podarilo nakodovat malu kuriozitku. Ono mi to az po case doslo, ze je to vlastne somarina ale funguje to, takze pre vsetkych, co maju zaujem a su zvedavi, pripajam k tomuto postu komplet zdrojovy kod. Je to v Borland C++ 3.1 (jeden maly modul). A co to vlastne je? Zadanie znelo urobit preemptivny multitasking. A prave to robi. Nic viac a nic menej. Jednoducho to nacita z disku jeden COM subor (patricne jednoduchy, lebo moj 'kernel' neswapuje obrazovku ani nic podobne, ved bezi v real mode) a ten dajme tomu spusti 20x v pamati a prepina medzi procesami (napr. frekvenciou 200Hz). Viem, ze to nie je nic moc ako program, ale hadzem to sem hlavne kvoli zberatelom kuriozit, zaciatocnikom a microsoftu. Pripajam aj jeden vzorovy COM subor (assembler) ako vzorovy proces pre to 'jadro' v cecku. Ovladanie programu je jasne zo zdrojaku. ('1' - insertprocess, 'ESC' - koniec) be Happy sayza PS: Skoro som zabudol ... treba pouzit LARGE model ;) (bcc -ml) ---------------------------------------------------------------------------- source PM.CPP, preklad bcc -ml PM.CPP ---------------------------------------------------------------------------- /* 1. Procesom sa vyhradi pamat a nacita sa proces PROCESS.COM 2. Vlozi sa prvy proces 3. inicializuje sa obsluha prerusenia 4. preda sa riadenie casovacu LOOP 1. pri preruseni sa zisti, ci sa prerusil nejaky proces (sluzby OS a BIOS sa neprerusuju) preda sa riadenie inemu procesu 2. ak sa stlaci klavesa '1' => insert noveho procesu 4. ak sa stlaci klavesa ESC => preda sa riadenie naspat MAINu *LOOP 8. uvolnenie pamati a obsluhy prerusenia */ #include <stdio.h> #include <conio.h> #include <dos.h> #include <stdlib.h> #include <io.h> typedef struct _REGISTERS { unsigned bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags; } REGISTERS; typedef struct _PCB { REGISTERS reg; unsigned priority; } PCB; #define MK_LINEAR(px) ( (long(FP_SEG(px))<<4) + long(FP_OFF(px)) ) #ifdef __cplusplus #define __CPPARGS ... #else #define __CPPARGS #endif #define PROCESS_LENGTH 256 #define MAX_PROCESS 25 PCB procesy[MAX_PROCESS]; //fronta procesov int nprocess,curprocess; char *pmemoryBase,*pmemoryLast,*pmemoryAlloc; long plinearBase,plinearLast; //segment*16+offset char *process; char maincall,done=0; void InsertProcess(void); // obsluha prerusenia ************************************** void interrupt ( *oldtimer)(__CPPARGS); void interrupt ( *oldkeyboard)(__CPPARGS); REGISTERS *stackr; REGISTERS mainr; #define ACKRET { outportb(0x20,0x20); return; } long linear; void interrupt _timer_handler(__CPPARGS) { // nastavim smernik tam, kde cecko pushlo registre stackr = (REGISTERS *)MK_FP(_SS,_SP); enable(); // ak som prerusil main (IBA raz) tak savnem jeho registre // a prejdem na curprocess if (maincall) { maincall=0; mainr = *stackr; *stackr = procesy[curprocess].reg; return; } // skontroljem, ci som prerusil moj proces linear = (long(stackr->cs)<<4) + long(stackr->ip); if (linear<plinearBase || linear>=plinearLast) ACKRET; // koncime .... if (done) { *stackr = mainr; ACKRET; } // skopirujem tieto registre do struktury aktualneho PCB procesy[curprocess].reg = *stackr; if (++curprocess>=nprocess) curprocess=0; *stackr = procesy[curprocess].reg; ACKRET; } void interrupt _keyboard_handler(__CPPARGS) { unsigned char al = inportb(0x60); if (al==1) done=1; // ESC if (al==2) InsertProcess(); // '1' al = inportb(0x61); outportb(0x61,al | 0x80); outportb(0x61,al); ACKRET; } void settimer(unsigned freq) { unsigned tim; if (freq==0) tim=0; else tim=0x1234DDlu/freq; outportb(0x43,0x34); outportb(0x40,tim&0xFF); outportb(0x40,tim>>8); } void main() { FILE *f; textmode(C80); clrscr(); // pridelenie pamati procesom (pre jednoduchost vsetkym naraz) pmemoryAlloc = (char *)malloc (64000); // cca jeden segment if (pmemoryAlloc==NULL) { puts("Neda sa alokovat 64000 bytov"); return; } // borland C++ alokuje ako SEGMENT:4, zarovnam teda pamat na hranicu segmentu pmemoryBase=(char *)MK_FP(FP_SEG(pmemoryAlloc)+1,0); pmemoryLast=pmemoryBase; plinearBase=MK_LINEAR(pmemoryBase); plinearLast=plinearBase; // nacitanie procesu do pamati (aby sa zakazdym necital v preruseni) // procesu sa potom vyhradi 256 bytov + 256 bytov hlavicka (COM subor) process = (char *)malloc(256+PROCESS_LENGTH); if ((f=fopen("process.com","rb"))==NULL) return; fread(process+256,filelength(fileno(f)),1,f); fclose(f); // vytvorenie jedneho PCB a jedneho procesu nprocess=curprocess=0; InsertProcess(); maincall=0; // instalacia obsluhy prerusenia oldkeyboard = getvect(0x9); setvect(0x9, _keyboard_handler); oldtimer = getvect(0x8); setvect(0x8, _timer_handler); settimer(100); maincall=1; geninterrupt(0x8); // umelo vytvorene prerusenie casovaca // tymto sa MAIN vzdava kontroly nad systemom :)) settimer(0); setvect(0x9,oldkeyboard); setvect(0x8,oldtimer); free(pmemoryAlloc); clrscr(); } #include <mem.h> void InsertProcess(void) { if (nprocess>=MAX_PROCESS) return; // pridelenie pamati // skopirujem proces do pamati a zmenim mu PID na adrese CS:0 memcpy(pmemoryLast,process,PROCESS_LENGTH+256); *pmemoryLast = nprocess; // nastavim mu inicializacne registre memset(&procesy[nprocess].reg,0,sizeof(REGISTERS)); procesy[nprocess].reg.cs=FP_SEG(pmemoryLast)+FP_OFF(pmemoryLast)/16; procesy[nprocess].reg.ds=procesy[nprocess].reg.es=procesy[nprocess].reg.cs; procesy[nprocess].reg.ip=0x100; procesy[nprocess].reg.flags=0x3256; // nejake rozumne flagy pmemoryLast+=PROCESS_LENGTH+256; plinearLast+=PROCESS_LENGTH+256; nprocess++; } ---------------------------------------------------------------------------- source PROCESS.ASM, preklad tasm /m2 PROCESS.ASM, tlink /t /x PROCESS.OBJ ---------------------------------------------------------------------------- ;------------------------------------------------------------------------- Code SEGMENT ASSUME CS:Code,DS:Code __start: org 100h .286 ;------------------------------------------------------------------------- ;STANDALONE EQU 1 LOOPCOUNT EQU 8;00 ; toto je experimentalne nastavena hodnota ... _start: jmp _main hlaska DB ' Vobec ma netesi, ze som proces ' ;// CO PROCES VYPISUJE pid DB ? _endln LABEL BYTE ;// ukoncuje hlasku dlzka DW OFFSET _endln - OFFSET hlaska ;// dlzka hlasky stlpec DW ? ;// na ktorom stlpci je scroll zaciatokriadku DW ? koniecriadku DW ? ;// adresa konca riadku _main: ;// proces si DS na svoj segment push cs pop ds ;// proces si nastavi PID ako znak (PID je na adrese CS:0) mov al,ds:[0] add al,'A' mov [pid],al ;// nastavi ES:BX na obrazovku mov ax,0B800h mov es,ax mov al,160 ;// kolko bytov je na riadok mov bl,ds:[0] ;// * poradie = offset mul bl mov [zaciatokriadku],ax ;// mam adresu zaciatku riadka add ax,160 mov [koniecriadku],ax mov [stlpec],0 ;// ... a zacne scrollovat loopscroll: mov cx,[dlzka] mov si,OFFSET hlaska mov di,[zaciatokriadku] mov ax,[stlpec] shr ax,8 add di,ax add di,ax print: movsb inc di dec cx jz koniecvypisu cmp di,[koniecriadku] jb print mov di,[zaciatokriadku] jmp print koniecvypisu: IFDEF STANDALONE mov ah,1 int 16h jnz quit ENDIF mov cx,LOOPCOUNT cakaj: loop cakaj inc [stlpec] cmp [stlpec],80*256 jb loopscroll mov [stlpec],0 jmp loopscroll ;// sem sa to pri multitaskingu nikdy nedostane quit: mov ax,0 int 16h mov ah,4ch int 21h ;------------------------------------------------------------------------- Code ENDS END _start