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