(NUMBER) " PER BRINCH HANSEN INFORMATION SCIENCE CALIFORNIA INSTITUTE OF TECHNOLOGY THE JOB STREAM SYSTEM 31 DECEMBER 1975 " "############# # IO TYPES # #############" TYPE IODEVICE = (TYPEDEVICE, DISKDEVICE, TAPEDEVICE, PRINTDEVICE, CARDDEVICE); TYPE IOOPERATION = (INPUT, OUTPUT, MOVE, CONTROL); TYPE IOARG = (WRITEEOF, REWIND, UPSPACE, BACKSPACE); TYPE IORESULT = (COMPLETE, INTERVENTION, TRANSMISSION, FAILURE, ENDFILE, ENDMEDIUM, STARTMEDIUM); TYPE IOPARAM = RECORD OPERATION: IOOPERATION; STATUS: IORESULT; ARG: IOARG END; CONST NL = '(:10:)'; FF = '(:12:)'; CR = '(:13:)'; EM = '(:25:)'; CONST LINELENGTH = 132; TYPE LINE = ARRAY (.1..LINELENGTH.) OF CHAR; CONST PAGELENGTH = 512; TYPE PAGE = ARRAY (.1..PAGELENGTH.) OF CHAR; "########################## # PROCESSQUEUE AND FIFO # ##########################" CONST PROCESSCOUNT = 8; TYPE PROCESSQUEUE = ARRAY (.1..PROCESSCOUNT.) OF QUEUE; TYPE FIFO = CLASS(LIMIT: INTEGER); VAR HEAD, TAIL, LENGTH: INTEGER; FUNCTION ENTRY ARRIVAL: INTEGER; BEGIN ARRIVAL:= TAIL; TAIL:= TAIL MOD LIMIT + 1; LENGTH:= LENGTH + 1; END; FUNCTION ENTRY DEPARTURE: INTEGER; BEGIN DEPARTURE:= HEAD; HEAD:= HEAD MOD LIMIT + 1; LENGTH:= LENGTH - 1; END; FUNCTION ENTRY EMPTY: BOOLEAN; BEGIN EMPTY:= (LENGTH = 0) END; FUNCTION ENTRY FULL: BOOLEAN; BEGIN FULL:= (LENGTH = LIMIT) END; BEGIN HEAD:= 1; TAIL:= 1; LENGTH:= 0 END; "############# # RESOURCE # #############" TYPE RESOURCE = MONITOR VAR FREE: BOOLEAN; Q: PROCESSQUEUE; NEXT: FIFO; PROCEDURE ENTRY REQUEST; BEGIN IF FREE THEN FREE:= FALSE ELSE DELAY(Q(.NEXT.ARRIVAL.)); END; PROCEDURE ENTRY RELEASE; BEGIN IF NEXT.EMPTY THEN FREE:= TRUE ELSE CONTINUE(Q(.NEXT.DEPARTURE.)); END; BEGIN FREE:= TRUE; INIT NEXT(PROCESSCOUNT) END; "################# # TYPERESOURCE # #################" TYPE TYPERESOURCE = MONITOR VAR FREE: BOOLEAN; Q: PROCESSQUEUE; NEXT: FIFO; HEADER: LINE; PROCEDURE ENTRY REQUEST(TEXT: LINE; VAR CHANGED: BOOLEAN); BEGIN IF FREE THEN FREE:= FALSE ELSE DELAY(Q(.NEXT.ARRIVAL.)); CHANGED:= (HEADER <> TEXT); HEADER:= TEXT; END; PROCEDURE ENTRY RELEASE; BEGIN IF NEXT.EMPTY THEN FREE:= TRUE ELSE CONTINUE(Q(.NEXT.DEPARTURE.)); END; BEGIN FREE:= TRUE; HEADER(.1.):= NL; INIT NEXT(PROCESSCOUNT); END; "############### # TYPEWRITER # ###############" TYPE TYPEWRITER = CLASS(DEVICE: IODEVICE); CONST LINELIMIT = 73; CANCELCHAR = '(:3:)'; "CONTROL C" CANCELLINE = '(:12:)'; "CONTROL L" PROCEDURE WRITECHAR(X: CHAR); VAR PARAM: IOPARAM; C: CHAR; BEGIN PARAM.OPERATION:= OUTPUT; C:= X; IO(C, PARAM, DEVICE); END; PROCEDURE ENTRY WRITE(TEXT: LINE); VAR PARAM: IOPARAM; I: INTEGER; C: CHAR; BEGIN PARAM.OPERATION:= OUTPUT; I:= 0; REPEAT I:= I + 1; C:= TEXT(.I.); IO(C, PARAM, DEVICE); UNTIL (C = NL) OR (I = LINELIMIT); IF C <> NL THEN WRITECHAR(NL); END; PROCEDURE ENTRY READ(VAR TEXT: LINE); CONST BEL = '(:7:)'; VAR PARAM: IOPARAM; I: INTEGER; C: CHAR; BEGIN WRITECHAR(BEL); PARAM.OPERATION:= INPUT; I:= 0; REPEAT IO(C, PARAM, DEVICE); IF C = CANCELLINE THEN BEGIN WRITECHAR(NL); WRITECHAR('?'); I:= 0; END ELSE IF C = CANCELCHAR THEN BEGIN IF I > 0 THEN BEGIN WRITECHAR('?'); I:= I - 1; END END ELSE BEGIN I:= I + 1; TEXT(.I.):= C END; UNTIL (C = NL) OR (I = LINELIMIT); IF C <> NL THEN BEGIN WRITECHAR(NL); TEXT(.LINELIMIT + 1.):= NL; END; END; BEGIN END; "############# # TERMINAL # #############" TYPE TERMINAL = CLASS(ACCESS: TYPERESOURCE); VAR UNIT: TYPEWRITER; PROCEDURE ENTRY READ(HEADER: LINE; VAR TEXT: LINE); VAR CHANGED: BOOLEAN; BEGIN ACCESS.REQUEST(HEADER, CHANGED); IF CHANGED THEN UNIT.WRITE(HEADER); UNIT.READ(TEXT); ACCESS.RELEASE; END; PROCEDURE ENTRY WRITE(HEADER, TEXT: LINE); VAR CHANGED: BOOLEAN; BEGIN ACCESS.REQUEST(HEADER, CHANGED); IF CHANGED THEN UNIT.WRITE(HEADER); UNIT.WRITE(TEXT); ACCESS.RELEASE; END; BEGIN INIT UNIT(TYPEDEVICE) END; "######### # DISK # #########" TYPE DISK = CLASS(TYPEUSE: TYPERESOURCE); VAR OPERATOR: TERMINAL; PROCEDURE TRANSFER(COMMAND: IOOPERATION; PAGEADDR: UNIV IOARG; VAR BLOCK: PAGE); VAR PARAM: IOPARAM; RESPONSE: LINE; BEGIN WITH PARAM, OPERATOR DO BEGIN OPERATION:= COMMAND; ARG:= PAGEADDR; IO(BLOCK, PARAM, DISKDEVICE); WHILE STATUS <> COMPLETE DO BEGIN WRITE('DISK:(:10:)', 'ERROR(:10:)'); READ('PUSH RETURN(:10:)', RESPONSE); IO(BLOCK, PARAM, DISKDEVICE); END; END; END; PROCEDURE ENTRY READ(PAGEADDR: INTEGER; VAR BLOCK: UNIV PAGE); BEGIN TRANSFER(INPUT, PAGEADDR, BLOCK) END; PROCEDURE ENTRY WRITE(PAGEADDR: INTEGER; VAR BLOCK: UNIV PAGE); BEGIN TRANSFER(OUTPUT, PAGEADDR, BLOCK) END; BEGIN INIT OPERATOR(TYPEUSE) END; "######################### # FILEMAP AND DISKFILE # #########################" CONST MAPLENGTH = 255; TYPE FILEMAP = RECORD FILELENGTH: INTEGER; PAGESET: ARRAY (.1..MAPLENGTH.) OF INTEGER END; TYPE DISKFILE = CLASS(TYPEUSE: TYPERESOURCE); VAR UNIT: DISK; MAP: FILEMAP; OPENED: BOOLEAN; ENTRY LENGTH: INTEGER; FUNCTION INCLUDES(PAGENO: INTEGER): BOOLEAN; BEGIN INCLUDES:= OPENED & (1 <= PAGENO) & (PAGENO <= LENGTH); END; PROCEDURE ENTRY OPEN(MAPADDR: INTEGER); BEGIN UNIT.READ(MAPADDR, MAP); LENGTH:= MAP.FILELENGTH; OPENED:= TRUE; END; PROCEDURE ENTRY CLOSE; BEGIN LENGTH:= 0; OPENED:= FALSE; END; PROCEDURE ENTRY READ(PAGENO: INTEGER; VAR BLOCK: UNIV PAGE); BEGIN IF INCLUDES(PAGENO) THEN UNIT.READ(MAP.PAGESET(.PAGENO.), BLOCK); END; PROCEDURE ENTRY WRITE(PAGENO: INTEGER; VAR BLOCK: UNIV PAGE); BEGIN IF INCLUDES(PAGENO) THEN UNIT.WRITE(MAP.PAGESET(.PAGENO.), BLOCK); END; BEGIN INIT UNIT(TYPEUSE); LENGTH:= 0; OPENED:= FALSE; END; "###################### # CATALOG STRUCTURE # ######################" CONST IDLENGTH = 12; TYPE IDENTIFIER = ARRAY (.1..IDLENGTH.) OF CHAR; TYPE FILEKIND = (EMPTY, SCRATCH, ASCII, SEQCODE, CONCODE); TYPE FILEATTR = RECORD KIND: FILEKIND; ADDR: INTEGER; PROTECTED: BOOLEAN; NOTUSED: ARRAY (.1..5.) OF INTEGER END; TYPE CATENTRY = RECORD ID: IDENTIFIER; ATTR: FILEATTR; KEY, SEARCHLENGTH: INTEGER END; CONST CATPAGELENGTH = 16; TYPE CATPAGE = ARRAY (.1..CATPAGELENGTH.) OF CATENTRY; CONST CATADDR = 154; "############## # DISKTABLE # ##############" TYPE DISKTABLE = CLASS(TYPEUSE: TYPERESOURCE; CATADDR: INTEGER); VAR FILE: DISKFILE; PAGENO: INTEGER; BLOCK: CATPAGE; ENTRY LENGTH: INTEGER; PROCEDURE ENTRY READ(I: INTEGER; VAR ELEM: CATENTRY); VAR INDEX: INTEGER; BEGIN INDEX:= (I - 1) DIV CATPAGELENGTH + 1; IF PAGENO <> INDEX THEN BEGIN PAGENO:= INDEX; FILE.READ(PAGENO, BLOCK); END; ELEM:= BLOCK(.(I - 1) MOD CATPAGELENGTH + 1.); END; BEGIN INIT FILE(TYPEUSE); FILE.OPEN(CATADDR); LENGTH:= FILE.LENGTH * CATPAGELENGTH; PAGENO:= 0; END; "################ # DISKCATALOG # ################" TYPE DISKCATALOG = MONITOR(TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATADDR: INTEGER); VAR TABLE: DISKTABLE; FUNCTION HASH(ID: IDENTIFIER): INTEGER; VAR KEY, I: INTEGER; C: CHAR; BEGIN KEY:= 1; I:= 0; REPEAT I:= I + 1; C:= ID(.I.); IF C <> ' ' THEN KEY:= KEY * ORD(C) MOD TABLE.LENGTH + 1; UNTIL (C = ' ') OR (I = IDLENGTH); HASH:= KEY; END; PROCEDURE ENTRY LOOKUP (ID: IDENTIFIER; VAR ATTR: FILEATTR; VAR FOUND: BOOLEAN); VAR KEY, MORE, INDEX: INTEGER; ELEM: CATENTRY; BEGIN DISKUSE.REQUEST; KEY:= HASH(ID); TABLE.READ(KEY, ELEM); MORE:= ELEM.SEARCHLENGTH; INDEX:= KEY; FOUND:= FALSE; WHILE NOT FOUND & (MORE > 0) DO BEGIN TABLE.READ(INDEX, ELEM); IF ELEM.ID = ID THEN BEGIN ATTR:= ELEM.ATTR; FOUND:= TRUE END ELSE BEGIN IF ELEM.KEY = KEY THEN MORE:= MORE - 1; INDEX:= INDEX MOD TABLE.LENGTH + 1; END; END; DISKUSE.RELEASE; END; BEGIN INIT TABLE(TYPEUSE, CATADDR) END; "################## # LOADERPROCESS # ##################" TYPE LOADERPROCESS= PROCESS(DISKUSE: RESOURCE); CONST SOLOADDR = 24; VAR PARAM: IOPARAM; PROCEDURE INITIALIZE(PAGENO: UNIV IOARG); BEGIN WITH PARAM DO BEGIN OPERATION:= CONTROL; ARG:= PAGENO; END; END; BEGIN INITIALIZE(SOLOADDR); "AWAIT BEL SIGNAL" IO(PARAM, PARAM, TYPEDEVICE); "LOAD SOLO SYSTEM" DISKUSE.REQUEST; IO(PARAM, PARAM, DISKDEVICE); DISKUSE.RELEASE; END; "############# # DATAFILE # #############" TYPE DATAFILE = CLASS(TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG); VAR FILE: DISKFILE; OPENED: BOOLEAN; ENTRY LENGTH: INTEGER; PROCEDURE ENTRY OPEN(ID: IDENTIFIER; VAR FOUND: BOOLEAN); VAR ATTR: FILEATTR; BEGIN CATALOG.LOOKUP(ID, ATTR, FOUND); IF FOUND THEN BEGIN DISKUSE.REQUEST; FILE.OPEN(ATTR.ADDR); LENGTH:= FILE.LENGTH; DISKUSE.RELEASE; END; OPENED:= FOUND; END; PROCEDURE ENTRY CLOSE; BEGIN FILE.CLOSE; LENGTH:= 0; OPENED:= FALSE; END; PROCEDURE ENTRY READ(PAGENO: INTEGER; VAR BLOCK: UNIV PAGE); BEGIN IF OPENED THEN BEGIN DISKUSE.REQUEST; FILE.READ(PAGENO, BLOCK); DISKUSE.RELEASE; END; END; PROCEDURE ENTRY WRITE(PAGENO: INTEGER; VAR BLOCK: UNIV PAGE); BEGIN IF OPENED THEN BEGIN DISKUSE.REQUEST; FILE.WRITE(PAGENO, BLOCK); DISKUSE.RELEASE; END; END; BEGIN INIT FILE(TYPEUSE); LENGTH:= 0; OPENED:= FALSE; END; "############### # PAGEBUFFER # ###############" TYPE PAGEBUFFER = MONITOR (TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG); VAR OPENED: BOOLEAN; BUFFER: DATAFILE; NEXT: FIFO; SENDER, RECEIVER: QUEUE; PROCEDURE ENTRY READ(VAR BLOCK: PAGE); BEGIN WITH BUFFER, NEXT DO IF OPENED THEN BEGIN IF EMPTY THEN DELAY(RECEIVER); READ(DEPARTURE, BLOCK); CONTINUE(SENDER); END; END; PROCEDURE ENTRY WRITE(VAR BLOCK: PAGE); BEGIN WITH BUFFER, NEXT DO IF OPENED THEN BEGIN IF FULL THEN DELAY(SENDER); WRITE(ARRIVAL, BLOCK); CONTINUE(RECEIVER); END; END; PROCEDURE ENTRY OPEN(ID: IDENTIFIER); BEGIN WITH BUFFER DO IF NOT OPENED THEN BEGIN OPEN(ID, OPENED); INIT NEXT(LENGTH); END; END; BEGIN INIT BUFFER(TYPEUSE, DISKUSE, CATALOG); OPENED:= FALSE; END; "################ # INPUTSTREAM # ################" TYPE INPUTSTREAM = CLASS(BUFFER: PAGEBUFFER); VAR TEXT: PAGE; COUNT: INTEGER; MORE: BOOLEAN; PROCEDURE ENTRY READ(VAR C: CHAR); BEGIN IF MORE THEN BEGIN IF COUNT = PAGELENGTH THEN BEGIN BUFFER.READ(TEXT); COUNT:= 0; END; COUNT:= SUCC(COUNT); C:= TEXT(.COUNT.); MORE:= (C <> EM); END ELSE C:= EM; END; PROCEDURE ENTRY NEXT; BEGIN MORE:= TRUE; BUFFER.READ(TEXT); COUNT:= 0; END; BEGIN MORE:= FALSE END; "################# # OUTPUTSTREAM # #################" TYPE OUTPUTSTREAM = CLASS(BUFFER: PAGEBUFFER); VAR TEXT: PAGE; COUNT: INTEGER; MORE: BOOLEAN; PROCEDURE ENTRY WRITE(C: CHAR); BEGIN IF MORE THEN BEGIN COUNT:= SUCC(COUNT); TEXT(.COUNT.):= C; IF (COUNT = PAGELENGTH) OR (C = EM) THEN BEGIN BUFFER.WRITE(TEXT); COUNT:= 0; MORE:= (C <> EM); END; END; END; PROCEDURE ENTRY NEXT; BEGIN MORE:= TRUE; COUNT:= 0; END; BEGIN MORE:= FALSE END; "################## # PROGRAM TYPES # ##################" TYPE ATTRINDEX = (CALLER, HEAPTOP, PROGLINE, PROGRESULT); TYPE ARGTAG = (NILTYPE, BOOLTYPE, INTTYPE, IDTYPE, PTRTYPE); TYPE ARGTYPE = RECORD TAG: ARGTAG; ARG: IDENTIFIER END; CONST MAXARG = 10; TYPE ARGLIST = ARRAY (.1..MAXARG.) OF ARGTYPE; TYPE RESULTTYPE = (TERMINATED, OVERFLOW, POINTERERROR, RANGEERROR, VARIANTERROR, HEAPLIMIT, STACKLIMIT, CODELIMIT, TIMELIMIT, CALLERROR); "########################### # PROGSTORE AND PROGFILE # ###########################" TYPE PROGSTATE = (READY, NOTFOUND, NOTSEQ, TOOBIG); CONST STORELENGTH1 = 40; TYPE PROGSTORE1 = ARRAY (.1..STORELENGTH1.) OF PAGE; TYPE PROGFILE1 = CLASS(TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG); VAR FILE: DISKFILE; ENTRY STORE: PROGSTORE1; PROCEDURE ENTRY OPEN(ID: IDENTIFIER; VAR STATE: PROGSTATE); VAR ATTR: FILEATTR; FOUND: BOOLEAN; PAGENO: INTEGER; BEGIN CATALOG.LOOKUP(ID, ATTR, FOUND); WITH DISKUSE, FILE, ATTR DO IF NOT FOUND THEN STATE:= NOTFOUND ELSE IF KIND <> SEQCODE THEN STATE:= NOTSEQ ELSE BEGIN REQUEST; OPEN(ADDR); IF LENGTH <= STORELENGTH1 THEN BEGIN FOR PAGENO:= 1 TO LENGTH DO READ(PAGENO, STORE(.PAGENO.)); STATE:= READY; END ELSE STATE:= TOOBIG; CLOSE; RELEASE; END; END; BEGIN INIT FILE(TYPEUSE); END; CONST STORELENGTH2 = 4; TYPE PROGSTORE2 = ARRAY (.1..STORELENGTH2.) OF PAGE; TYPE PROGFILE2 = CLASS(TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG); VAR FILE: DISKFILE; ENTRY STORE: PROGSTORE2; PROCEDURE ENTRY OPEN(ID: IDENTIFIER; VAR STATE: PROGSTATE); VAR ATTR: FILEATTR; FOUND: BOOLEAN; PAGENO: INTEGER; BEGIN CATALOG.LOOKUP(ID, ATTR, FOUND); WITH DISKUSE, FILE, ATTR DO IF NOT FOUND THEN STATE:= NOTFOUND ELSE IF KIND <> SEQCODE THEN STATE:= NOTSEQ ELSE BEGIN REQUEST; OPEN(ADDR); IF LENGTH <= STORELENGTH2 THEN BEGIN FOR PAGENO:= 1 TO LENGTH DO READ(PAGENO, STORE(.PAGENO.)); STATE:= READY; END ELSE STATE:= TOOBIG; CLOSE; RELEASE; END; END; BEGIN INIT FILE(TYPEUSE); END; "############## # PROGTIMER # ##############" TYPE PROGTIMER = MONITOR VAR WHO, TIMELEFT: INTEGER; RUNNING: BOOLEAN; PROCEDURE ENTRY LIMIT(MAXTIME: INTEGER); BEGIN WHO:= ATTRIBUTE(CALLER); TIMELEFT:= MAXTIME; END; PROCEDURE ENTRY TICK; BEGIN TIMELEFT:= TIMELEFT - 1; IF (TIMELEFT <= 0) & RUNNING THEN BEGIN STOP(WHO, TIMELIMIT); RUNNING:= FALSE; END; END; PROCEDURE ENTRY ENTERPROG; BEGIN RUNNING:= TRUE END; PROCEDURE ENTRY ENDPROG; BEGIN RUNNING:= FALSE; START; END; BEGIN TIMELEFT:= 0; RUNNING:= FALSE; END; "################# # CLOCKPROCESS # #################" TYPE CLOCKPROCESS = PROCESS(TIMER: PROGTIMER); BEGIN CYCLE WAIT; TIMER.TICK; END; END; "############### # FILE NAMES # ###############" CONST JOBPREFIX = 'JOBPREFIX '; JOBINPUT = 'JOBINPUT '; JOBOUTPUT = 'JOBOUTPUT '; JOBSERVICE = 'JOBSERVICE '; JOBBUFFER1 = 'JOBBUFFER1 '; JOBBUFFER2 = 'JOBBUFFER2 '; JOB = 'JOB '; TEMP1 = 'TEMP1 '; TEMP2 = 'TEMP2 '; "############### # LINEBUFFER # ###############" TYPE LINEBUFFER = MONITOR CONST MAXLINE = 20; TYPE LINES = ARRAY (.1..MAXLINE.) OF LINE; VAR BUFFER: LINES; NEXT: FIFO; SENDER, RECEIVER: QUEUE; PROCEDURE ENTRY READ(VAR TEXT: LINE); BEGIN WITH NEXT DO BEGIN IF EMPTY THEN DELAY(RECEIVER); TEXT:= BUFFER(.DEPARTURE.); CONTINUE(SENDER); END; END; PROCEDURE ENTRY WRITE(TEXT: LINE); BEGIN WITH NEXT DO BEGIN IF FULL THEN DELAY(SENDER); BUFFER(.ARRIVAL.):= TEXT; CONTINUE(RECEIVER); END; END; BEGIN INIT NEXT(MAXLINE) END; "################ # CARDPROCESS # ################" TYPE CARDPROCESS = PROCESS (TYPEUSE: TYPERESOURCE; BUFFER: LINEBUFFER); VAR OPERATOR: TERMINAL; TEXT: LINE; PARAM: IOPARAM; OK: BOOLEAN; BEGIN INIT OPERATOR(TYPEUSE); PARAM.OPERATION:= INPUT; CYCLE REPEAT IO(TEXT, PARAM, CARDDEVICE); CASE PARAM.STATUS OF COMPLETE: OK:= TRUE; INTERVENTION, FAILURE: BEGIN OK:= FALSE; WAIT END; TRANSMISSION: BEGIN OPERATOR.WRITE('CARDS: (:10:)', 'ERROR(:10:)'); OK:= FALSE; END END; UNTIL OK; BUFFER.WRITE(TEXT); END; END; "################# # INPUTPROCESS # #################" TYPE INPUTPROCESS = PROCESS (TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG; INBUFFER: LINEBUFFER; OUTBUFFER: PAGEBUFFER); "PROGRAM DATA SPACE = " +1000 VAR OPERATOR: TERMINAL; PREFIX: DATAFILE; CODE: PROGFILE2; PROGRAM DRIVER(STORE: PROGSTORE2); ENTRY PREFIXLENGTH, READPREFIX, READLINE, WRITESTREAM; FUNCTION ENTRY PREFIXLENGTH: INTEGER; BEGIN PREFIXLENGTH:= PREFIX.LENGTH END; PROCEDURE ENTRY READPREFIX (PAGENO: INTEGER; VAR BLOCK: PAGE); BEGIN PREFIX.READ(PAGENO, BLOCK) END; PROCEDURE ENTRY READLINE(VAR TEXT: LINE); BEGIN INBUFFER.READ(TEXT) END; PROCEDURE ENTRY WRITESTREAM(VAR BLOCK: PAGE); BEGIN OUTBUFFER.WRITE(BLOCK) END; PROCEDURE INITIALIZE; VAR FOUND: BOOLEAN; STATE: PROGSTATE; BEGIN INIT OPERATOR(TYPEUSE), PREFIX(TYPEUSE, DISKUSE, CATALOG), CODE(TYPEUSE, DISKUSE, CATALOG); PREFIX.OPEN(JOBPREFIX, FOUND); CODE.OPEN(JOBINPUT, STATE); IF STATE = READY THEN DRIVER(CODE.STORE); OPERATOR.WRITE('JOB INPUT: (:10:)', 'TERMINATED (:10:)'); END; BEGIN INITIALIZE END; "############### # JOBPROCESS # ###############" TYPE JOBPROCESS = PROCESS (TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG; INBUFFER, OUTBUFFER: PAGEBUFFER; TIMER: PROGTIMER); "PROGRAM DATA SPACE = " +16000 CONST MAXFILE = 2; TYPE FILE = 1..MAXFILE; VAR INSTREAM: INPUTSTREAM; OUTSTREAM: OUTPUTSTREAM; FILES: ARRAY (.FILE.) OF DATAFILE; CODE: PROGFILE1; DIGITS, SIGN, NUMERIC: SET OF CHAR; MININTEGER: INTEGER; PROGRAM PASCAL(STORE: PROGSTORE1); ENTRY READ, WRITE, WRITEINT, WRITETEXT, OPEN, CLOSE, GET, PUT, LENGTH, RUNPASS, RUNJOB; PROGRAM PASS(VAR PARAM: ARGLIST; STORE: PROGSTORE1); ENTRY READ, WRITE, OPEN, CLOSE, GET, PUT, LENGTH, MARK, RELEASE; PROGRAM USER(STORE: PROGSTORE1); ENTRY READ, WRITE, READINT, WRITEINT, WRITETEXT; PROCEDURE ENTRY READ(VAR C: CHAR); BEGIN INSTREAM.READ(C) END; PROCEDURE ENTRY WRITE(C: CHAR); BEGIN OUTSTREAM.WRITE(C) END; PROCEDURE ENTRY READINT(VAR VALUE: INTEGER); VAR POSITIVE, OVERFLOW: BOOLEAN; C: CHAR; DIGIT: INTEGER; BEGIN WITH INSTREAM DO BEGIN REPEAT READ(C) UNTIL C IN NUMERIC; IF C IN SIGN THEN BEGIN POSITIVE:= (C = '+'); READ(C); END ELSE POSITIVE:= TRUE; OVERFLOW:= FALSE; VALUE:= 0; WHILE NOT OVERFLOW & (C IN DIGITS) DO BEGIN DIGIT:= ORD(C) - ORD('0'); IF VALUE < (MININTEGER + DIGIT) DIV 10 THEN OVERFLOW:= TRUE ELSE VALUE:= 10*VALUE - DIGIT; READ(C); END; WHILE C IN DIGITS DO READ(C); IF POSITIVE THEN IF VALUE = MININTEGER THEN OVERFLOW:= TRUE ELSE VALUE:= - VALUE; END; IF OVERFLOW THEN STOP(ATTRIBUTE(CALLER), RANGEERROR); END; PROCEDURE ENTRY WRITEINT(VALUE, LENGTH: INTEGER); VAR NUMBER: ARRAY (.1..6.) OF CHAR; DIGITS, REMAINDER, I: INTEGER; BEGIN WITH OUTSTREAM DO BEGIN REMAINDER:= VALUE; DIGITS:= 0; REPEAT DIGITS:= DIGITS + 1; NUMBER(.DIGITS.):= CHR(ABS(REMAINDER MOD 10) + ORD('0')); REMAINDER:= REMAINDER DIV 10; UNTIL REMAINDER = 0; FOR I:= 1 TO LENGTH - DIGITS - 1 DO WRITE(' '); IF VALUE < 0 THEN WRITE('-') ELSE WRITE(' '); FOR I:= DIGITS DOWNTO 1 DO WRITE(NUMBER(.I.)); END; END; PROCEDURE ENTRY WRITETEXT(TEXT: LINE); VAR CHARNO: INTEGER; C: CHAR; BEGIN WITH OUTSTREAM DO BEGIN CHARNO:= 1; C:= TEXT(.1.); WHILE (C <> '#') & (CHARNO < LINELENGTH) DO BEGIN WRITE(C); CHARNO:= CHARNO + 1; C:= TEXT(.CHARNO.); END; END; END; PROCEDURE ENTRY OPEN(FILENO: FILE; ID: IDENTIFIER; VAR FOUND: BOOLEAN); BEGIN FILES(.FILENO.).OPEN(ID, FOUND) END; PROCEDURE ENTRY CLOSE(FILENO: FILE); BEGIN FILES(.FILENO.).CLOSE END; PROCEDURE ENTRY GET(FILENO: FILE; PAGENO: INTEGER; VAR BLOCK: PAGE); BEGIN FILES(.FILENO.).READ(PAGENO, BLOCK) END; PROCEDURE ENTRY PUT(FILENO: FILE; PAGENO: INTEGER; VAR BLOCK: PAGE); BEGIN FILES(.FILENO.).WRITE(PAGENO, BLOCK) END; FUNCTION ENTRY LENGTH(FILENO: FILE): INTEGER; BEGIN LENGTH:= FILES(.FILENO.).LENGTH END; PROCEDURE ENTRY MARK(VAR TOP: INTEGER); BEGIN TOP:= ATTRIBUTE(HEAPTOP) END; PROCEDURE ENTRY RELEASE(TOP: INTEGER); BEGIN SETHEAP(TOP) END; PROCEDURE ENTRY RUNPASS (ID: IDENTIFIER; VAR PARAM: ARGLIST; VAR LINE, RESULT: UNIV INTEGER); CONST TERMINATED = 0; VAR STATE: PROGSTATE; HEAPADDR: INTEGER; BEGIN WITH CODE, TIMER DO BEGIN OPEN(ID, STATE); ENTERPROG; HEAPADDR:= ATTRIBUTE(HEAPTOP); PASS(PARAM, STORE); LINE:= ATTRIBUTE(PROGLINE); RESULT:= ATTRIBUTE(PROGRESULT); IF RESULT <> TERMINATED THEN SETHEAP(HEAPADDR); ENDPROG; OPEN(JOBSERVICE, STATE); END; END; PROCEDURE ENTRY RUNJOB (VAR LINE, RESULT: UNIV INTEGER); VAR STATE: PROGSTATE; HEAPADDR: INTEGER; BEGIN WITH CODE, TIMER DO BEGIN OPEN(JOB, STATE); ENTERPROG; HEAPADDR:= ATTRIBUTE(HEAPTOP); USER(STORE); LINE:= ATTRIBUTE(PROGLINE); RESULT:= ATTRIBUTE(PROGRESULT); SETHEAP(HEAPADDR); ENDPROG; OPEN(JOBSERVICE, STATE); END; END; PROCEDURE NEXTJOB; CONST MAXTIME = 60 "SECONDS"; VAR STATE: PROGSTATE; HEAPADDR: INTEGER; C: CHAR; BEGIN WITH CODE, TIMER DO BEGIN INSTREAM.NEXT; OUTSTREAM.NEXT; LIMIT(MAXTIME); OPEN(JOBSERVICE, STATE); HEAPADDR:= ATTRIBUTE(HEAPTOP); PASCAL(STORE); SETHEAP(HEAPADDR); REPEAT INSTREAM.READ(C) UNTIL C = EM; WITH OUTSTREAM DO BEGIN WRITE(NL); WRITE(EM) END; END; END; PROCEDURE INITIALIZE; VAR F: FILE; BEGIN INIT INSTREAM(INBUFFER), OUTSTREAM(OUTBUFFER); FOR F:= 1 TO MAXFILE DO INIT FILES(.F.)(TYPEUSE, DISKUSE, CATALOG); INIT CODE(TYPEUSE, DISKUSE, CATALOG); DIGITS:= (.'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'.); SIGN:= (.'+', '-'.); NUMERIC:= DIGITS OR SIGN OR (.EM.); MININTEGER:= -32767 - 1; END; BEGIN INITIALIZE; CYCLE NEXTJOB END; END; "################## # OUTPUTPROCESS # ##################" TYPE OUTPUTPROCESS = PROCESS (TYPEUSE: TYPERESOURCE; DISKUSE: RESOURCE; CATALOG: DISKCATALOG; INBUFFER: PAGEBUFFER; OUTBUFFER: LINEBUFFER); "PROGRAM DATA SPACE = " +1000 VAR OPERATOR: TERMINAL; PREFIX: DATAFILE; CODE: PROGFILE2; PROGRAM DRIVER(STORE: PROGSTORE2); ENTRY PREFIXLENGTH, READPREFIX, READSTREAM, WRITELINE; FUNCTION ENTRY PREFIXLENGTH: INTEGER; BEGIN PREFIXLENGTH:= PREFIX.LENGTH END; PROCEDURE ENTRY READPREFIX (PAGENO: INTEGER; VAR BLOCK: PAGE); BEGIN PREFIX.READ(PAGENO, BLOCK) END; PROCEDURE ENTRY READSTREAM(VAR BLOCK: PAGE); BEGIN INBUFFER.READ(BLOCK) END; PROCEDURE ENTRY WRITELINE(TEXT: LINE); BEGIN OUTBUFFER.WRITE(TEXT) END; PROCEDURE INITIALIZE; VAR FOUND: BOOLEAN; STATE: PROGSTATE; BEGIN INIT OPERATOR(TYPEUSE), PREFIX(TYPEUSE, DISKUSE, CATALOG), CODE(TYPEUSE, DISKUSE, CATALOG); PREFIX.OPEN(JOBPREFIX, FOUND); CODE.OPEN(JOBOUTPUT, STATE); IF STATE = READY THEN DRIVER(CODE.STORE); OPERATOR.WRITE('JOB OUTPUT:(:10:)', 'TERMINATED (:10:)'); END; BEGIN INITIALIZE END; "################### # PRINTERPROCESS # ###################" TYPE PRINTERPROCESS = PROCESS (TYPEUSE: TYPERESOURCE; BUFFER: LINEBUFFER); VAR OPERATOR: TERMINAL; PARAM: IOPARAM; TEXT: LINE; BEGIN INIT OPERATOR(TYPEUSE); PARAM.OPERATION:= OUTPUT; CYCLE BUFFER.READ(TEXT); IO(TEXT, PARAM, PRINTDEVICE); IF PARAM.STATUS <> COMPLETE THEN BEGIN OPERATOR.WRITE('PRINTER: (:10:)', 'INSPECT(:10:)'); REPEAT WAIT; IO(TEXT, PARAM, PRINTDEVICE); UNTIL PARAM.STATUS = COMPLETE; END; END; END; "#################### # INITIAL PROCESS # ####################" VAR DISKUSE: RESOURCE; TYPEUSE: TYPERESOURCE; OPERATOR: TERMINAL; CATALOG: DISKCATALOG; WATCHDOG: LOADERPROCESS; INBUFFER, OUTBUFFER: PAGEBUFFER; TIMER: PROGTIMER; CLOCK: CLOCKPROCESS; CARDBUFFER, PRINTERBUFFER: LINEBUFFER; READER: CARDPROCESS; PRODUCER: INPUTPROCESS; MASTER: JOBPROCESS; CONSUMER: OUTPUTPROCESS; WRITER: PRINTERPROCESS; FUNCTION EXISTS(FILE: IDENTIFIER; KIND: FILEKIND): BOOLEAN; VAR ATTR: FILEATTR; FOUND: BOOLEAN; BEGIN CATALOG.LOOKUP(FILE, ATTR, FOUND); EXISTS:= FOUND & (ATTR.KIND = KIND); END; BEGIN INIT DISKUSE, TYPEUSE, OPERATOR(TYPEUSE), CATALOG(TYPEUSE, DISKUSE, CATADDR), WATCHDOG(DISKUSE); IF EXISTS(JOBPREFIX, ASCII) & EXISTS(JOBINPUT, SEQCODE) & EXISTS(JOBSERVICE, SEQCODE) & EXISTS(JOBOUTPUT, SEQCODE) & EXISTS(JOB, SEQCODE) & EXISTS(JOBBUFFER1, SCRATCH) & EXISTS(JOBBUFFER2, SCRATCH) & EXISTS(TEMP1, SCRATCH) & EXISTS(TEMP2, SCRATCH) THEN BEGIN INIT INBUFFER(TYPEUSE, DISKUSE, CATALOG), OUTBUFFER(TYPEUSE, DISKUSE, CATALOG); INBUFFER.OPEN(JOBBUFFER1); OUTBUFFER.OPEN(JOBBUFFER2); INIT TIMER, CLOCK(TIMER), CARDBUFFER, PRINTERBUFFER, READER(TYPEUSE, CARDBUFFER), PRODUCER(TYPEUSE, DISKUSE, CATALOG, CARDBUFFER, INBUFFER), MASTER(TYPEUSE, DISKUSE, CATALOG, INBUFFER, OUTBUFFER, TIMER), CONSUMER(TYPEUSE, DISKUSE, CATALOG, OUTBUFFER, PRINTERBUFFER), WRITER(TYPEUSE, PRINTERBUFFER); END ELSE OPERATOR.WRITE('JOB STREAM:(:10:)', 'FILES MISSING(:10:)'); END.