]> pd.if.org Git - mmurtl/commitdiff
autocommit for file dated 1995-01-21 15:26:36
authorRichard Burgess <>
Sat, 21 Jan 1995 15:26:36 +0000 (15:26 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Mon, 17 Oct 2016 14:03:47 +0000 (14:03 +0000)
ossource/kernel.asm [new file with mode: 0644]

diff --git a/ossource/kernel.asm b/ossource/kernel.asm
new file mode 100644 (file)
index 0000000..c0b8eb2
--- /dev/null
@@ -0,0 +1,1840 @@
+;   MMURTL Operating System Source Code\r
+;   Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+;   ALL RIGHTS RESERVED\r
+;   Version 1.0\r
+\r
+.DATA\r
+.INCLUDE MOSEDF.INC\r
+.INCLUDE TSS.INC\r
+.INCLUDE RQB.INC\r
+.INCLUDE JOB.INC\r
+\r
+dJunk   DD 0           ;Used as temp in Service Abort function\r
+dmsg    DD 2 DUP(0) ;tmp for abort\r
+\r
+EXTRN TimerTick  DD\r
+EXTRN SwitchTick DD\r
+EXTRN dfHalted   DD\r
+EXTRN _nSwitches DD\r
+EXTRN _nHalts    DD\r
+EXTRN _nReady    DD\r
+\r
+.CODE\r
+\r
+EXTRN LinToPhy NEAR\r
+\r
+;This file contains all the internal kernel functions plus\r
+;the PUBLIC kernel functions SendMsg, ISendMsg, WaitMsg, CheckMsg,\r
+;Request, Respond, MoveMsg, NewTask and SpawnTask.\r
+;Exchange management functions such as AllocExch and DeAllocExch are\r
+;also here.\r
+;\r
+; Note on interrupts and the kernel primitives:\r
+; Because certain kernel functions may be called from ISRs,\r
+; and because portions of other kernel functions may be\r
+; interrupted by a task change that happens because of an action\r
+; that an ISR takes, we must ensure that interrupts are\r
+; DISABLED prior to the allocation or deallocation\r
+; of ALL kernel data segment resources. This especially applies when\r
+; a message is "in transit."  For example: taken from an exchange\r
+; but not yet linked to a TSS and placed on the ready queue.\r
+; This is important!\r
+;\r
+; NOTE on Exchanges, Messages, and Tasks (TSSs)\r
+; In MMURTL, an exchange is a place where either Messages or\r
+; Tasks wait.  There can never be tasks AND messages at an\r
+; exchange at the same time (unless the kernel is BROKEN!).\r
+; When a message is sent to an exchange, if a task is waiting\r
+; there, is it immediately associated with the message\r
+; and placed on the readyQ in priority order.\r
+; For this reason we share the HEAD and TAIL link pointers\r
+; for tasks and messages on an exchange.\r
+;\r
+;=============================================================================\r
+\r
+enQueueMsg:\r
+;\r
+; INPUT : ESI,EAX\r
+; OUTPUT : NONE\r
+; REGISTERS : EAX,EDX,ESI,FLAGS\r
+; MODIFIES : EDX\r
+;\r
+; This routine will place the link block pointed to by EAX onto the exchange\r
+; pointed to by the ESI register. If EAX is NIL then the routine returns.\r
+;\r
+               OR  EAX,EAX                             ; if pLBin = NIL THEN Return;\r
+               JZ eqMsgDone            ;\r
+               MOV DWORD PTR [EAX+NextLB], 0   ; pLBin^.Next <= NIL;\r
+               XCHG ESI,EAX            ; pExch => EAX, pLBin => ESI\r
+               CMP DWORD PTR [EAX+EHead], 0    ; if ..MsgHead = NIL\r
+               JNE eqMNotNIL           ; then\r
+               MOV [EAX+EHead],ESI     ;  ..MsgHead <= pLBin;\r
+               MOV [EAX+ETail],ESI     ;  ..MsgTail <= pLBin;\r
+               MOV DWORD PTR [EAX+fEMsg], 1            ; Flag it as a Msg (vice a task)\r
+               XCHG EAX,ESI            ; Put pExch Back in ESI\r
+               RETN                    ; else\r
+eqMNotNIL:\r
+           MOV EDX,[EAX+ETail]     ;  ..MsgTail^.NextLB <= pLBin;\r
+               MOV [EDX+NextLB],ESI    ;\r
+               MOV [EAX+ETail],ESI     ;  ..MsgTail <= pLBin;\r
+               MOV DWORD PTR [EAX+fEMsg], 1            ; Flag it as a Msg (vice a task)\r
+               XCHG EAX,ESI            ; Put pExch Back in ESI\r
+eqMsgDone:\r
+           RETN                    ;\r
+\r
+;=============================================================================\r
+\r
+deQueueMsg:\r
+;\r
+; INPUT : ESI\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ESI,FLAGS\r
+; MODIFIES : *prgExch[ESI].msg.head and EBX\r
+;\r
+; This routine will dequeue a link block on the exchange pointed to by the\r
+; ESI register and place the pointer to the link block dequeued into EAX.\r
+;\r
+               MOV EAX,[ESI+fEMsg]     ; Get Msg Flag\r
+               OR EAX, EAX                             ; Is it a Msg?\r
+               JZ deMsgDone                    ; No! (return 0)\r
+               MOV EAX,[ESI+EHead]     ; pLBout <= ..MsgHead;\r
+               OR EAX, EAX             ; if pLBout = NIL then Return;\r
+               JZ deMsgDone            ;\r
+               MOV EBX,[EAX+NextLB]    ; ..MsgHead <= ..MsgHead^.Next;\r
+               MOV [ESI+EHead],EBX     ;\r
+deMsgDone:\r
+           RETN                    ;\r
+\r
+;=============================================================================\r
+\r
+deQueueTSS:\r
+;\r
+; INPUT : ESI\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ESI,FLAGS\r
+; MODIFIES : EAX,EBX\r
+;\r
+; This routine will dequeue a TSS on the exchange pointed to by the ESI\r
+; register and place the pointer to the TSS dequeued into EAX.\r
+; EAX return NIL if no TSS is waiting at Exch ESI\r
+;\r
+               XOR EAX,EAX                             ; Set up to return nothing\r
+               MOV EBX,[ESI+fEMsg]             ; Msg flag (is it a Msg)\r
+               OR EBX, EBX\r
+               JNZ deTSSDone                   ; It's a Msg (return leaving EAX 0)\r
+               MOV EAX,[ESI+EHead]     ; pTSSout <= ..TSSHead;\r
+               OR EAX, EAX             ; if pTSSout = NIL then Return;\r
+               JZ deTSSDone            ;\r
+               MOV EBX,[EAX+NextTSS]   ; ..TSSHead <= ..TSSHead^.Next;\r
+               MOV [ESI+EHead],EBX     ;\r
+deTSSDone:\r
+           RETN                        ;\r
+\r
+;=============================================================================\r
+\r
+PUBLIC enQueueRdy:\r
+;\r
+; INPUT : EAX\r
+; OUTPUT : NONE\r
+; REGISTERS : EAX,EBX,EDX,FLAGS\r
+; MODIFIES : EAX,EBX,EDX\r
+;\r
+; This routine will place a TSS pointed to by EAX onto the ReadyQueue. This\r
+; algorithm chooses the proper priority queue based on the TSS priority.\r
+; The Rdy Queue is an array of QUEUES (2 pointers, head & tail per QUEUE).\r
+; This links the TSS to rgQueue[nPRI].\r
+;\r
+               OR  EAX,EAX             ; if pTSS = NIL then return;\r
+               JZ eqRdyDone            ;\r
+               INC _nReady                             ;\r
+               MOV DWORD PTR [EAX+NextTSS], 0  ; pTSSin^.Next <= NIL;\r
+               XOR EBX,EBX             ; get the priority\r
+               MOV BL,[EAX+Priority]   ; in EBX\r
+               XCHG EAX,EBX            ; Priority => EAX, pTSSin => EBX\r
+               SHL EAX, 3              ; Times 8 (size of QUEUE)\r
+               LEA EDX,RdyQ            ; Add offset of RdyQ => EAX\r
+               ADD EAX,EDX             ; EAX pts to proper Rdy Queue\r
+               CMP DWORD PTR [EAX+Head], 0      ; if Head = NIL\r
+               JNE eqRNotNIL           ; then\r
+               MOV [EAX+Head],EBX      ;  ..Head <= pTSSin;\r
+               MOV [EAX+Tail],EBX      ;  ..Tail <= pTSSin;\r
+               RETN                    ; else\r
+eqRNotNIL:\r
+           MOV EDX,[EAX+Tail]      ;  ..Tail^.NextTSS <= pTSSin;\r
+               MOV [EDX+NextTSS],EBX   ;\r
+               MOV [EAX+Tail],EBX      ;  ..Tail <= pTSSin;\r
+eqRdyDone:\r
+           RETN                    ;\r
+\r
+;=============================================================================\r
+\r
+PUBLIC deQueueRdy:\r
+;\r
+; INPUT : NONE\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ECX,FLAGS\r
+; MODIFIES : RdyQ\r
+;\r
+; This routine will return a pointer in EAX to the highest priority task\r
+; queued on the RdyQ. Then the routine will "pop" the TSS from the RdyQ.\r
+; If there was no task queued, EAX is returned as NIL.\r
+;\r
+               MOV ECX,nPRI            ; Set up the number of times to loop\r
+               LEA EBX,RdyQ            ; Get base address of RdyQ in EBX\r
+deRdyLoop:\r
+           MOV EAX,[EBX]           ; Get pTSSout in EAX\r
+               OR  EAX, EAX            ; IF pTSSout is NIL Then go and\r
+               JNZ deRdyFound          ; check the next priority.\r
+               ADD EBX,sQUEUE          ; Point to the next Priority Queue\r
+               LOOP deRdyLoop          ; DEC ECX and LOOP IF NOT ZERO\r
+deRdyFound:\r
+               OR  EAX, EAX            ; IF pTSSout is NIL Then there are\r
+               JZ deRdyDone            ; No TSSs on the RdyQ; RETURN\r
+               DEC _nReady                             ;\r
+               MOV ECX,[EAX+NextTSS]   ; Otherwise, deQueue the process\r
+               MOV [EBX],ECX           ; And return with the pointer in EAX\r
+deRdyDone:\r
+           RETN                    ;\r
+\r
+;=============================================================================\r
+\r
+PUBLIC ChkRdyQ:\r
+;\r
+; INPUT : NONE\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ECX,FLAGS\r
+; MODIFIES : RdyQ\r
+;\r
+; This routine will return a pointer to the highest priority TSS that\r
+; is queued to run. It WILL NOT remove it from the Queue.\r
+; If there was no task queued, EAX is returned as NIL.\r
+;\r
+               MOV ECX,nPRI            ; Set up the number of times to loop\r
+               LEA EBX,RdyQ            ; Get base address of RdyQ in EBX\r
+ChkRdyLoop:\r
+           MOV EAX,[EBX]           ; Get pTSSout in EAX\r
+               OR  EAX, EAX            ; IF pTSSout is NIL Then go and\r
+               JNZ ChkRdyDone          ; check the next priority.\r
+               ADD EBX,sQUEUE          ; Point to the next Priority Queue\r
+               LOOP ChkRdyLoop       ; DEC ECX and LOOP IF NOT ZERO\r
+ChkRdyDone:\r
+           RETN                    ;\r
+\r
+;=============================================================================\r
+;================= BEGIN NEAR KERNEL HELPER ROUTINES =========================\r
+;=============================================================================\r
+\r
+; RemoveRdyJob  (NEAR)\r
+;\r
+; This routine searchs all ready queue priorities for tasks belonging\r
+; to pJCB. When one is found it is removed from the queue\r
+; and the TSS is freed up.  This is called when we are killing\r
+; a job.\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              RemoveRdyJob(char *pJCB):ercType\r
+;\r
+;      pJCB is a pointer to the JCB that the tasks to kill belong to.\r
+;\r
+; pJCB                 EQU DWORD PTR [EBP+8]\r
+;\r
+; INPUT :  (pJCB on stack)\r
+; OUTPUT : NONE\r
+; REGISTERS : All general registers are trashed\r
+; MODIFIES : RdyQ\r
+;\r
+;\r
+PUBLIC _RemoveRdyJob:\r
+;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV ECX,nPRI            ; Set up the number of times to loop\r
+               LEA EBX,RdyQ            ; Get base address of RdyQ in EBX\r
+           MOV EDX, [EBP+8]            ; EDX holds pJCB for comparison (RAB)\r
+\r
+               ;EBX points to begining of next Priority Queue\r
+RemRdyLoop:\r
+           MOV EAX,[EBX+Head]      ; Get pTSS in EAX\r
+               MOV EDI, EAX                    ; EDI points to last TSS by default (or NIL)\r
+               OR  EAX,EAX             ; Is pTSS 0 (none left queued here)\r
+               JNZ RemRdy0                     ; Valid pTSS!\r
+RemRdyLoop1:\r
+               MOV [EBX+Tail], EDI             ; EDI always points to last TSS or NIL\r
+               ADD EBX,sQUEUE          ; Point to the next Priority Queue\r
+               LOOP RemRdyLoop         ; DEC ECX and LOOP IF NOT ZERO\r
+\r
+               XOR EAX, EAX                    ; No error\r
+               POP EBP\r
+               RETN 4                                  ; All done (clean stack)\r
+\r
+               ;Go here to dequeue a TSS at head of list\r
+RemRdy0:\r
+               CMP EDX, [EAX+TSS_pJCB] ; Is this from the JCB we want?\r
+               JNE RemRdy2                             ; No\r
+\r
+               MOV EDI, [EAX+NextTSS]  ; Yes, deQueue the TSS\r
+               MOV [EBX+Head], EDI     ; Fix link in Queue list\r
+\r
+               PUSH EBX                                ; Save ptr to RdyQue (crnt priority)\r
+\r
+               ;Free up the TSS (add it to the free list)\r
+               MOV EBX,pFreeTSS        ; pTSSin^.Next <= pFreeTSS;\r
+               MOV [EAX+NextTSS],EBX   ;\r
+               MOV DWORD PTR [EAX+TSS_pJCB], 0 ; Make TSS invalid\r
+               MOV pFreeTSS,EAX        ; pFreeTSS <= pTSSin;\r
+               INC _nTSSLeft                   ;\r
+\r
+               POP EBX\r
+               MOV EAX, EDI                ; Make EAX point to new head TSS\r
+               OR EAX, EAX                             ; Is it Zero?\r
+               JZ RemRdyLoop1                  ; Next Queue please\r
+               JMP RemRdy0                             ; back to check next at head of list\r
+\r
+               ;Go here to dequeue a TSS in middle or end of list\r
+RemRdy2:\r
+               MOV EAX, [EDI+NextTSS]  ; Get next link in list\r
+               OR EAX, EAX                             ; Valid pTSS?\r
+               JZ RemRdyLoop1                  ; No. Next Queue please\r
+               CMP EDX, [EAX+TSS_pJCB] ; Is this from JCB we want?\r
+               JE RemRdy3                              ; Yes. Trash it.\r
+               MOV     EDI, EAX                        ; No. Next TSS\r
+               JMP RemRdy2\r
+RemRdy3:\r
+               ;EDI points to prev TSS\r
+               ;EAX points to crnt TSS\r
+               ;Make ESI point to NextTSS\r
+\r
+               MOV ESI, [EAX+NextTSS]  ; Yes, deQueue the TSS\r
+\r
+               ;Now we fix the list (Make Prev point to Next)\r
+               ;This extracts EAX from the list\r
+\r
+               MOV [EDI+NextTSS], ESI  ;Jump the removed link\r
+               PUSH EBX                                ;Save ptr to RdyQue (crnt priority)\r
+\r
+               ;Free up the TSS (add it to the free list)\r
+               MOV EBX,pFreeTSS        ; pTSSin^.Next <= pFreeTSS;\r
+               MOV [EAX+NextTSS],EBX                   ;\r
+               MOV DWORD PTR [EAX+TSS_pJCB], 0 ; Make TSS invalid\r
+               MOV pFreeTSS,EAX                ; pFreeTSS <= pTSSin;\r
+               INC _nTSSLeft                                   ;\r
+\r
+               POP EBX\r
+               ;\r
+               OR  ESI, ESI                    ;Is EDI the new Tail? (ESI = 0)\r
+               JZ  RemRdyLoop1                 ;Yes. Next Queue please\r
+               JMP RemRdy2                             ;back to check next TSS\r
+\r
+\r
+;=============================================================================\r
+; GetExchOwner  (NEAR)\r
+;\r
+; This routine returns the owner of the exchange specified.\r
+; A pointer to the JCB of the owner is returned.\r
+; ErcNotAlloc is returned if the exchange isn't allocated.\r
+; ErcOutofRange is returned is the exchange number is invalid (too high)\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              GetExchOwner(long Exch, char *pJCBRet): dErrror\r
+;\r
+;      Exch is the exchange number.\r
+;      pJCBRet is a pointer to the JCB that the tasks to kill belong to.\r
+;\r
+; Exch         EQU DWORD PTR [EBP+12]\r
+; pJCBRet      EQU DWORD PTR [EBP+8]\r
+\r
+PUBLIC _GetExchOwner:          ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               MOV EAX, [EBP+12]               ; Get Resp Exchange in EDX\r
+               CMP EAX,nExch           ; Is the exchange out of range?\r
+               JB GEO01                    ; No, continue\r
+               MOV EAX,ErcOutOfRange   ; Yes, Error in EAX register\r
+               JMP GEOEnd                              ;\r
+GEO01:\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ; sExch * Exch number\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EDX,EAX             ; EDX -> Exch\r
+               MOV EAX, [EDX+Owner]\r
+               OR EAX, EAX                             ; Valid Exch (Allocated)\r
+               JNZ GEO02\r
+               MOV EAX, ErcNotAlloc    ; No, not allocated\r
+               JMP SHORT GEOEnd\r
+GEO02:\r
+               MOV ESI, [EBP+8]                ;Where to return pJCB of Exchange\r
+               MOV [ESI], EAX                  ;\r
+               XOR EAX, EAX\r
+GEOEnd:\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETN 8                  ;\r
+\r
+;=============================================================================\r
+; SetExchOwner  (NEAR)\r
+;\r
+; This routine sets the owner of the exchange specified to the\r
+; pJCB specified. This is used by the Job code to set the owner of\r
+; a TSS exchange to a new JCB (even though the exchange was allocated\r
+; by the OS).  No error checking is done as the job code does it upfront!\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              SetExchOwner(long Exch, char *pNewJCB): dErrror\r
+;\r
+;      Exch is the exchange number.\r
+;      pNewJCB is a pointer to the JCB of the new owner.\r
+;\r
+; Exch         EQU DWORD PTR [EBP+12]\r
+; pNewJCB      EQU DWORD PTR [EBP+8]\r
+\r
+PUBLIC _SetExchOwner:                  ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV EAX, [EBP+12]               ; Exchange Number\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ; sExch * Exch number\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ; EAX -> oExch + prgExch\r
+               MOV EBX, [EBP+8]\r
+               MOV [EAX+Owner], EBX\r
+               XOR EAX, EAX\r
+               POP EBP                 ;\r
+               RETN 8                  ;\r
+\r
+;=============================================================================\r
+; SendAbort  (NEAR)\r
+;\r
+; This routine sends one abort message to each valid service\r
+; with the jobnum of the aborting job. If we receive a\r
+; kernel error on Request it may be becuase it is a service\r
+; that is aborting itself. We ignore the kernel errors.\r
+;\r
+; Procedureal Interface :\r
+;\r
+;              SendAbort(long JobNum, ValidExch): dErrror\r
+;\r
+;      JobNum is the job that is aborting\r
+;      ValidExch is any valid exchange so the request will go through\r
+;\r
+; JobNum       EQU DWORD PTR [EBP+12]\r
+; ValidExch    EQU DWORD PTR [EBP+8]\r
+\r
+PUBLIC _SendAbort:              ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               MOV ESI,OFFSET rgSVC    ; Get the address of rgSVC\r
+               MOV ECX,nSVC                    ; Get the number of Service Descriptors\r
+SAB01:\r
+               CMP DWORD PTR [ESI], 0  ; Valid name?\r
+               JE SAB05                                ; NO, next service\r
+\r
+               PUSH ESI                                ;Save count and pointer to SVC name\r
+               PUSH ECX\r
+\r
+               ;Push all the params to make the request\r
+               PUSH ESI                                ;pName\r
+               PUSH 0                                  ;Abort Service Code\r
+               MOV EAX, [EBP+8]                ;Exchange\r
+               PUSH EAX\r
+               PUSH OFFSET dJunk               ;pHandleRet\r
+               PUSH 0                                  ;npSend\r
+               PUSH 0                                  ;pData0\r
+               PUSH 0                                  ;cbData0\r
+               PUSH 0                                  ;pData1\r
+               PUSH 0                                  ;cbData1\r
+               MOV EAX, [EBP+12]               ;JobNum\r
+               PUSH EAX                                ;dData0\r
+               PUSH 0                                  ;dData1\r
+               PUSH 0                                  ;dData2\r
+               CALL FWORD PTR _Request\r
+\r
+;RAB\r
+SAB02:\r
+               PUSH DWORD PTR [EBP+8]\r
+               PUSH OFFSET     dmsg\r
+               CALL FWORD PTR _WaitMsg\r
+               MOV EAX, dmsg\r
+               CMP EAX, dJunk          ;see if we got got response back!\r
+               JNE SAB02                       ;NO - wait again\r
+;RAB\r
+\r
+               POP ECX\r
+               POP ESI\r
+SAB05:\r
+               ADD ESI, sSVC                   ;Next Service name\r
+               LOOP SAB01\r
+               XOR EAX, EAX\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETN 8                  ;\r
+\r
+\r
+\r
+;=======================================================================\r
+;============== BEGIN PUBLIC KERNEL PRIMITIVES =========================\r
+;=======================================================================\r
+;\r
+; Request - The kernel request primitive sends a message like the Send\r
+; primitive except this function requires several more parameters.\r
+; A system structure called a request block is allocated and some of these\r
+; parameters are placed in it.  A request block is the basic\r
+; structure used for Client-Server communications.  The exchange where a\r
+; request should be queued is determined by searching the system service\r
+; array for a matching request service name specified in the request block.\r
+; The procedural interface to Request looks like this:\r
+;\r
+;    Request(  pSvcName     [EBP+56]\r
+;              wSvcCode     [EBP+52]\r
+;              dRespExch    [EBP+48]\r
+;              pRqHndlRet   [EBP+44]\r
+;              dnpSend      [EBP+40]\r
+;              pData1       [EBP+36]\r
+;              dcbData1     [EBP+32]\r
+;              pData2       [EBP+28]\r
+;              dcbData2     [EBP+24]\r
+;              dData0       [EBP+20]\r
+;              dData1       [EBP+16]\r
+;              dData2       [EBP+12]  ) : dError\r
+\r
+\r
+PUBLIC __Request:                              ;\r
+               PUSH EBP                                ; Save the Previous FramePtr\r
+               MOV EBP,ESP                             ; Set up New FramePtr\r
+\r
+               ;Validate service name from registry and get exchange\r
+               MOV EAX, [EBP+56]               ;pServiceName\r
+               CALL GetExchange                ;Leaves Service Exch in ESI if no errors\r
+               OR  EAX,EAX                             ;Any errors?\r
+               JZ SHORT Req02                  ;No\r
+               JMP ReqEnd                              ;Yes, return error\r
+Req02:\r
+               ;Validate exchange\r
+               MOV EDX, [EBP+48]               ; Get Resp Exchange in EDX\r
+               CMP EDX,nExch           ; Is the exchange out of range?\r
+               JB Req03                    ; No, continue\r
+               MOV EAX,ercOutOfRange   ; Yes, Error in EAX register\r
+               JMP ReqEnd                              ;\r
+Req03:\r
+               ;Get them a request block\r
+               CLI\r
+               CALL NewRQB                             ;EAX has ptr to new RqBlk (or 0 if none)\r
+               STI\r
+               OR  EAX, EAX                    ;Did we get one? (NIL (0) means we didn't)\r
+               JNZ Req04                               ;Yes. EAX ptr to new RqBlk\r
+               MOV EAX, ErcNoMoreRqBlks ;No, Sorry...\r
+               JMP ReqEnd                              ;\r
+Req04:\r
+               ;ESI still has the exchange for the service\r
+               ;EDX still has the response exchange\r
+               ;EAX has pRqBlk (Handle)\r
+\r
+               MOV EBX, EAX                            ;EBX now pts to RqBlk\r
+               MOV [EBX+ServiceExch], ESI      ;Put Svc Exch into RqBlk\r
+               MOV EAX, [EBP+52]                       ;Get Svc Code\r
+               MOV [EBX+ServiceCode], AX       ;Put Svc Code into RqBlk\r
+               MOV [EBX+RespExch], EDX         ;Put Resp Exch into RqBlk\r
+               CALL GetCrntJobNum                      ;Get crnt JCB (Job Num of owner)\r
+               MOV [EBX+RqOwnerJob], EAX       ;put in RqBlk\r
+               MOV EAX, [EBP+20]                       ;Get dData0\r
+               MOV [EBX+dData0], EAX           ;put in RqBlk\r
+               MOV EAX, [EBP+16]                       ;Get dData1\r
+               MOV [EBX+dData1], EAX           ;put in RqBlk\r
+               MOV EAX, [EBP+20]                       ;Get dData2\r
+               MOV [EBX+dData2], EAX           ;put in RqBlk\r
+               MOV EAX, [EBP+36]                       ;Get pData1\r
+               MOV [EBX+pData1], EAX           ;put in RqBlk\r
+               MOV EAX, [EBP+32]                       ;Get cbData1\r
+               MOV [EBX+cbData1], EAX          ;put in RqBlk\r
+               MOV EAX, [EBP+28]                       ;Get pData2\r
+               MOV [EBX+pData2], EAX           ;put in RqBlk\r
+               MOV EAX, [EBP+24]                       ;Get cbData2\r
+               MOV [EBX+cbData2], EAX          ;put in RqBlk\r
+               MOV EAX, [EBP+40]                       ;Number of Send PbCbs\r
+               CMP EAX, 3                                      ;Must be 2 or less\r
+               JB Req06                                        ;\r
+               MOV EAX, 2\r
+Req06:\r
+               MOV [EBX+npSend], AL            ;Put nSend PbCbs into RqBlk\r
+               MOV CL, 2                                       ;Caculate nRecv (2-nSend)\r
+               SUB CL, AL                                      ;Leave in CL\r
+               MOV [EBX+npRecv], CL            ;Put npRecv in RqBlk\r
+\r
+               ;At this point the RqBlk is all filled in.\r
+               ;Now we will return the RqBlkHandle to the user.\r
+               ;The handle is actually a ptr to the RqBlk but they can't use\r
+               ;it as one anyway (so no problem)\r
+\r
+        MOV EDI, [EBP+44]                      ;Ptr to return handle to\r
+        MOV [EDI], EBX                         ;Give it to them\r
+        MOV EDX, EBX                           ;Save RqBlk in EDX\r
+\r
+               CLI                                                     ; No interruptions from here on\r
+               ;Now we allocate a Link block to use\r
+\r
+               MOV EAX,pFreeLB                 ; EAX <= pFreeLB;\r
+               OR EAX,EAX                      ; Is pFreeLB NIL? (out of LBs)\r
+               JNZ Req08                       ;\r
+               CALL DisposeRQB                         ; NO... free up RqBlk\r
+               MOV EAX,ercNoMoreLBs            ; Move error in the EAX register\r
+               JMP ReqEnd                                      ; Go home with bad news\r
+Req08:\r
+               MOV EBX,[EAX+NextLB]            ; pFreeLB <= pFreeLB^.Next\r
+               MOV pFreeLB,EBX                 ;\r
+               DEC _nLBLeft                            ;\r
+\r
+        MOV DWORD PTR [EAX+LBType],REQLB       ; This is a Request Link Block\r
+               MOV DWORD PTR [EAX+NextLB], 0           ; pLB^.Next <= NIL;\r
+               MOV [EAX+DataLo],EDX                            ; RqHandle into Lower 1/2 of Msg\r
+               MOV DWORD PTR [EAX+DataHi], 0           ; Store zero in upper half of pLB^.Data\r
+               PUSH EAX                                ; Save pLB on the stack\r
+\r
+               ;ESI still has the exchange Number for the service.\r
+               ;The ptr to the exch is required for deQueueTSS so we get it.\r
+\r
+        MOV EAX,ESI             ; Exch => EAX\r
+               MOV EDX, sEXCH          ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX             ; MAKE ESI <= pExch\r
+\r
+               ;ESI now points to the exchange\r
+\r
+               CALL deQueueTSS                 ; DeQueue a TSS on that Exch\r
+               OR  EAX,EAX                     ; Did we get one?\r
+               JNZ Req10                       ; Yes, give up the message\r
+               POP EAX                         ; No, Get the pLB just saved\r
+               CALL enQueueMsg                 ; EnQueue the Message on Exch\r
+               XOR EAX,EAX                                     ; No Error\r
+               JMP SHORT ReqEnd                ; And get out!\r
+Req10:\r
+        POP EBX                        ; Get the pLB just saved into EBX\r
+               MOV [EAX+pLBRet],EBX        ; and put it in the TSS\r
+               CALL enQueueRdy                 ; EnQueue the TSS on the RdyQ\r
+               MOV EAX,pRunTSS                         ; Get the Ptr To the Running TSS\r
+               CALL enQueueRdy                 ; and put him on the RdyQ\r
+               CALL deQueueRdy                 ; Get high priority TSS off the RdyQ\r
+               CMP EAX,pRunTSS                 ; If the high priority TSS is the\r
+               JNE Req12                       ; same as the Running TSS then return\r
+        XOR EAX,EAX                    ; Return to Caller with erc ok.\r
+               JMP SHORT ReqEnd\r
+Req12:\r
+        MOV pRunTSS,EAX         ; Make the TSS in EAX the Running TSS\r
+               MOV BX,[EAX+Tid]        ; Get the task Id (TR)\r
+               MOV TSS_Sel,BX          ; Put it in the JumpAddr for Task Swtich\r
+               INC _nSwitches                  ; Keep track of how many swtiches for stats\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]     ; JMP TSS (This is the task swtich)\r
+        XOR EAX,EAX             ; Return to Caller with erc ok.\r
+ReqEnd:\r
+               STI                     ;\r
+               MOV ESP,EBP                             ;\r
+               POP EBP                                 ;\r
+               RETF 48                                 ; Rtn to Caller & Remove Params from stack\r
+\r
+;=============================================================================\r
+; The response primitive is used by system services to respond to a\r
+; Request received at their service exchange.  The RqBlk handle must be\r
+; supplied along with the error/status code to be returned to the\r
+; caller.  This is very similar to Send except is dealiases addresses\r
+; in the RqBlk and then deallocates it.  The exchange to respond to\r
+; is located inside the RqBlk.\r
+; If dStatRet is ErcOwnerAbort, simply return the Reqest Block\r
+; to the free pool and return Erc 0 to caller.\r
+;     Respond(dRqHndl, dStatRet): dError\r
+;\r
+;\r
+dRqHndl         EQU DWORD PTR [EBP+16]\r
+dStatRet EQU DWORD PTR [EBP+12]\r
+\r
+PUBLIC __Respond:                              ;\r
+               PUSH EBP                                ; Save Callers Frame\r
+               MOV EBP,ESP                             ; Setup Local Frame\r
+;RAB\r
+               MOV EAX, dRqHndl                ; pRqBlk into EAX\r
+               MOV EBX, dStatRet\r
+               CMP EBX, ErcOwnerAbort  ;\r
+               JNE Resp01\r
+               CLI                                             ; No interruptions\r
+               CALL DisposeRQB                 ; Return Aborted RQB to pool.\r
+               XOR EAX, EAX            ; No Error\r
+               JMP RespEnd                             ; Get out\r
+Resp01:\r
+;RAB\r
+               MOV ESI, [EAX+RespExch] ; Response Exchange into ESI\r
+               CMP ESI,nExch           ; Is the exchange out of range?\r
+               JNAE Resp02             ; No, continue\r
+               MOV EAX,ercOutOfRange   ; Error into the EAX register.\r
+               JMP RespEnd                             ; Get out\r
+Resp02:\r
+        MOV EAX,ESI             ; Exch => EAX\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX             ; MAKE ESI <= pExch\r
+               CMP DWORD PTR [EAX+Owner], 0    ; If the exchange is not allocated\r
+               JNE Resp04              ; return to the caller with error\r
+               MOV EAX,ercNotAlloc     ; in the EAX register.\r
+               JMP RespEnd                             ;\r
+Resp04:\r
+               MOV EAX, dRqHndl        ; Get Request handle into EBX (pRqBlk)\r
+               MOV EBX, [EAX+RqOwnerJob]\r
+               CALL GetCrntJobNum\r
+               CMP EAX, EBX\r
+               JE Resp06                               ;Same job - no DeAlias needed\r
+\r
+               MOV EAX, dRqHndl        ; Get Request handle into EBX (pRqBlk)\r
+               MOV EBX, [EAX+cbData1]  ;\r
+               OR EBX, EBX\r
+               JZ Resp05                               ;No need to dealias (zero bytes)\r
+               MOV EDX, [EAX+pData1]\r
+               OR EDX, EDX\r
+               JZ Resp05                               ;Null pointer!\r
+\r
+               PUSH ESI                                ;Save pExch across call\r
+\r
+               PUSH EDX                                ;pMem\r
+               PUSH EBX                                ;cbMem\r
+               CALL GetCrntJobNum\r
+               PUSH EAX\r
+               CALL FWORD PTR _DeAliasMem      ;DO it and ignore errors\r
+               POP ESI                                 ;get pExch back\r
+Resp05:\r
+               MOV EAX, dRqHndl        ; Get Request handle into EBX (pRqBlk)\r
+               MOV EBX, [EAX+cbData2]  ;\r
+               OR EBX, EBX\r
+               JZ Resp06                               ;No need to dealias (zero bytes)\r
+               MOV EDX, [EAX+pData2]\r
+               OR EDX, EDX\r
+               JZ Resp06                               ;Null pointer!\r
+               PUSH ESI                                ;Save pExch across call\r
+\r
+               PUSH EDX                                ;pMem\r
+               PUSH EBX                                ;cbMem\r
+               CALL GetCrntJobNum              ;\r
+               PUSH EAX\r
+               CALL FWORD PTR _DeAliasMem      ;DO it and ignore errors\r
+               POP ESI                                 ;get pExch back\r
+Resp06:\r
+               MOV EAX, dRqHndl        ; Get Request handle into EBX (pRqBlk)\r
+               CLI                                             ; No interruptions\r
+               CALL DisposeRQB                 ; Return Rqb to pool. Not needed anymore\r
+\r
+               ; Allocate a link block\r
+               MOV EAX,pFreeLB         ; NewLB <= pFreeLB;\r
+               OR EAX,EAX              ; IF pFreeLB=NIL THEN No LBs;\r
+               JNZ Resp07              ;\r
+               MOV EAX,ercNoMoreLBs    ; caller with error in the EAX register\r
+               JMP RespEnd\r
+Resp07:\r
+               MOV EBX,[EAX+NextLB]    ; pFreeLB <= pFreeLB^.Next\r
+               MOV pFreeLB,EBX         ;\r
+               DEC _nLBLeft                    ;\r
+\r
+        MOV DWORD PTR [EAX+LBType], RESPLB ; This is a Response Link Block\r
+               MOV DWORD PTR [EAX+NextLB], 0      ; pLB^.Next <= NIL;\r
+               MOV EBX, dRqHndl        ; Get Request handle into EBX\r
+               MOV [EAX+DataLo],EBX    ; Store in lower half of pLB^.Data\r
+               MOV EBX, dStatRet       ; Get Status/Error into EBX\r
+               MOV [EAX+DataHi],EBX    ; Store in upper half of pLB^.Data\r
+               PUSH EAX                ; Save pLB on the stack\r
+               CALL deQueueTSS         ; DeQueue a TSS on that Exch\r
+               OR  EAX,EAX             ; Did we get one?\r
+               JNZ Resp08              ; Yes, give up the message\r
+               POP EAX                 ; Get the pLB just saved\r
+               CALL enQueueMsg         ; EnQueue the Message on Exch\r
+               XOR EAX, EAX                    ; No Error\r
+               JMP SHORT RespEnd       ; And get out!\r
+Resp08:\r
+        POP EBX                 ; Get the pLB just saved into EBX\r
+               MOV [EAX+pLBRet],EBX    ; and put it in the TSS\r
+               CALL enQueueRdy         ; EnQueue the TSS on the RdyQ\r
+               MOV EAX,pRunTSS         ; Get the Ptr To the Running TSS\r
+               CALL enQueueRdy         ; and put him on the RdyQ\r
+               CALL deQueueRdy         ; Get high priority TSS off the RdyQ\r
+\r
+               CMP EAX,pRunTSS         ; If the high priority TSS is the\r
+               JNE Resp10              ; same as the Running TSS then return\r
+               XOR EAX,EAX             ; Return to Caller with erc ok.\r
+               JMP SHORT RespEnd               ;\r
+Resp10:\r
+        MOV pRunTSS,EAX         ; Make the TSS in EAX the Running TSS\r
+               MOV BX,[EAX+Tid]        ; Get the task Id (TR)\r
+               MOV TSS_Sel,BX          ; Put it in the JumpAddr\r
+               INC _nSwitches\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]     ; JMP TSS\r
+        XOR EAX,EAX             ; Return to Caller with erc ok.\r
+RespEnd:\r
+               STI\r
+               MOV ESP,EBP                             ;\r
+               POP EBP                                 ;\r
+               RETF 8                                  ; Rtn to Caller & Remove Params\r
+\r
+;=============================================================================\r
+;\r
+; MoveRequest - The kernel Move Request primitive.\r
+; This allows a service to move a request to another exchange it owns.\r
+; This can not be used to forward a request to another service or Job.\r
+; It is very similar to send except it checks to ensure the destination\r
+; Exchange is owned by the sender.\r
+;\r
+; Procedural Interface :\r
+;\r
+;      MoveRequest(dRqBlkHndl, DestExch):ercType\r
+;\r
+;           dqMsg is the handle of the RqBlk to forward.\r
+;           DestExch the exchange to where the Request should be sent.\r
+;\r
+;\r
+;dRqBlkHndl     EQU [EBP+16]\r
+;DestExch              EQU [EBP+12]\r
+\r
+PUBLIC __MoveRequest:            ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV ESI, [EBP+12]       ; Get Exchange Parameter in ESI\r
+               CMP ESI,nExch           ; Is the exchange is out of range\r
+               JNAE MReq02             ; No, continue\r
+               MOV EAX,ercOutOfRange   ; in the EAX register.\r
+               JMP MReqEnd                             ; Get out\r
+MReq02:\r
+        MOV EAX,ESI             ; Exch => EAX\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX             ; MAKE ESI <= pExch\r
+               MOV EDX, [EAX+Owner]    ; Put exch owner into EDX (pJCB)\r
+               CALL GetpCrntJCB                ; Leaves it in EAX (uses only EAX)\r
+               CMP EDX, EAX                ; If the exchange is not owned by sender\r
+               JE  MReq04              ; return to the caller with error\r
+               MOV EAX, ErcNotOwner    ; in the EAX register.\r
+               JMP MReqEnd                             ; Get out\r
+MReq04:\r
+               CLI                                             ; No interruptions from here on\r
+               ; Allocate a link block\r
+               MOV EAX,pFreeLB         ; NewLB <= pFreeLB;\r
+               OR EAX,EAX              ; IF pFreeLB=NIL THEN No LBs;\r
+               JNZ MReq08              ;\r
+               MOV EAX,ercNoMoreLBs    ; caller with error in the EAX register\r
+               JMP MReqEnd                             ; Go home with bad news\r
+MReq08:\r
+               MOV EBX,[EAX+NextLB]    ; pFreeLB <= pFreeLB^.Next\r
+               MOV pFreeLB,EBX         ;\r
+               DEC _nLBLeft                    ;\r
+\r
+        MOV DWORD PTR [EAX+LBType], REQLBA     ; Request Link Block (ALIASED! RAB)\r
+               MOV DWORD PTR [EAX+NextLB], 0           ; pLB^.Next <= NIL;\r
+               MOV EBX, [EBP+16]                                       ; RqHandle\r
+               MOV [EAX+DataLo],EBX                            ; RqHandle into Lower 1/2 of Msg\r
+               MOV DWORD PTR [EAX+DataHi], 0           ; Store zero in upper half of pLB^.Data\r
+               PUSH EAX                        ; Save pLB on the stack\r
+               CALL deQueueTSS                 ; DeQueue a TSS on that Exch\r
+               OR  EAX,EAX                     ; Did we get one?\r
+               JNZ MReq10                      ; Yes, give up the message\r
+               POP EAX                         ; Get the pLB just saved\r
+               CALL enQueueMsg                 ; EnQueue the Message on Exch ESI\r
+               XOR EAX, EAX\r
+               JMP SHORT MReqEnd               ; And get out!\r
+MReq10:\r
+        POP EBX                        ; Get the pLB just saved into EBX\r
+               MOV [EAX+pLBRet],EBX        ; and put it in the TSS\r
+               CALL enQueueRdy                 ; EnQueue the TSS on the RdyQ\r
+               MOV EAX,pRunTSS                         ; Get the Ptr To the Running TSS\r
+               CALL enQueueRdy                 ; and put him on the RdyQ\r
+               CALL deQueueRdy                 ; Get high priority TSS off the RdyQ\r
+               CMP EAX,pRunTSS                 ; If the high priority TSS is the\r
+               JNE MReq12                      ; same as the Running TSS then return\r
+               XOR EAX,EAX                             ; Return to Caller with erc ok.\r
+               JMP SHORT MReqEnd\r
+MReq12:\r
+        MOV pRunTSS,EAX         ; Make the TSS in EAX the Running TSS\r
+               MOV BX,[EAX+Tid]        ; Get the task Id (TR)\r
+               MOV TSS_Sel,BX          ; Put it in the JumpAddr for Task Swtich\r
+               INC _nSwitches                  ; Keep track of how many swtiches for stats\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]     ; JMP TSS (This is the task switch)\r
+        XOR EAX,EAX             ; Return to Caller with erc ok.\r
+MReqEnd:\r
+               STI                     ;\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+\r
+;=============================================================================\r
+;\r
+; SendMsg - The kernel send primitive. This    sends a non-specific message\r
+; from a running task to an exchange. This may cause a task swtich if\r
+; a task is waiting at the exchange and it is of equal or higher priority\r
+; that the task that sent the message.\r
+;\r
+; Procedural Interface :\r
+;\r
+;       SendMsg(exch, dMsg1, dMsg2):ercType\r
+;\r
+;           exch is a DWORD (4 BYTES) containing the exchange to where the\r
+;           message should be sent.\r
+;\r
+;           dMsg1 & dMsg2 are DWord values defined and understood\r
+;                      only by the sending and receiving tasks.\r
+;\r
+SendExchange   EQU [EBP+14h]\r
+MessageHi      EQU DWORD PTR [EBP+10h]\r
+MessageLo      EQU DWORD PTR [EBP+0Ch]\r
+\r
+PUBLIC __SendMsg:                       ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV ESI,SendExchange    ; Get Exchange Parameter in ESI\r
+               CMP ESI,nExch           ; If the exchange is out of range\r
+               JNAE Send00             ; the return to caller with error\r
+               MOV EAX,ercOutOfRange   ; in the EAX register.\r
+               JMP SendEnd\r
+Send00:\r
+        MOV EAX,ESI             ; Exch => EAX\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX             ; MAKE ESI <= pExch\r
+               CMP DWORD PTR [EAX+Owner], 0    ; If the exchange is not allocated\r
+               JNE Send01              ; return to the caller with error\r
+               MOV EAX,ercNotAlloc     ; in the EAX register.\r
+               JMP SendEnd                             ;\r
+Send01:\r
+               CLI                                             ; No interrupts\r
+               ; Allocate a link block\r
+               MOV EAX,pFreeLB         ; NewLB <= pFreeLB;\r
+               OR EAX,EAX              ; IF pFreeLB=NIL THEN No LBs;\r
+               JNZ SHORT Send02        ;\r
+               MOV EAX,ercNoMoreLBs    ; caller with error in the EAX register\r
+               JMP SHORT MReqEnd               ; Go home with bad news\r
+Send02:\r
+               MOV EBX,[EAX+NextLB]    ; pFreeLB <= pFreeLB^.Next\r
+               MOV pFreeLB,EBX         ;\r
+               DEC _nLBLeft                    ;\r
+\r
+        MOV DWORD PTR [EAX+LBType], DATALB ; This is a Data Link Block\r
+               MOV DWORD PTR [EAX+NextLB], 0      ; pLB^.Next <= NIL;\r
+               MOV EBX,MessageLo       ; Get lower half of Msg in EBX\r
+               MOV [EAX+DataLo],EBX    ; Store in lower half of pLB^.Data\r
+               MOV EBX,MessageHi       ; Get upper half of Msg in EBX\r
+               MOV [EAX+DataHi],EBX    ; Store in upper half of pLB^.Data\r
+\r
+               PUSH EAX                ; Save pLB on the stack\r
+\r
+               CLI                                             ; No interrupts\r
+               CALL deQueueTSS         ; DeQueue a TSS on that Exch\r
+               STI\r
+               OR  EAX,EAX             ; Did we get one?\r
+               JNZ Send25              ; Yes, give up the message\r
+               POP EAX                 ; Get the pLB just saved\r
+               CLI                                             ; No interrupts\r
+               CALL enQueueMsg         ; EnQueue the Message on Exch\r
+               JMP Send04              ; And get out (Erc 0)!\r
+Send25:\r
+        POP EBX                 ; Get the pLB just saved into EBX\r
+               CLI                                             ; No interrupts\r
+               MOV [EAX+pLBRet],EBX    ; and put it in the TSS\r
+               CALL enQueueRdy         ; EnQueue the TSS on the RdyQ\r
+               MOV EAX,pRunTSS         ; Get the Ptr To the Running TSS\r
+               CALL enQueueRdy         ; and put him on the RdyQ\r
+               CALL deQueueRdy         ; Get high priority TSS off the RdyQ\r
+               CMP EAX,pRunTSS         ; If the high priority TSS is the\r
+               JNE Send03              ; same as the Running TSS then return\r
+               JMP SHORT Send04                ; Return with ErcOk\r
+\r
+Send03:\r
+        MOV pRunTSS,EAX         ; Make the TSS in EAX the Running TSS\r
+               MOV BX,[EAX+Tid]        ; Get the task Id (TR)\r
+               MOV TSS_Sel,BX          ; Put it in the JumpAddr\r
+               INC _nSwitches\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]     ; JMP TSS\r
+Send04:\r
+        XOR EAX,EAX             ; Return to Caller with erc ok.\r
+SendEnd:\r
+               STI                     ;\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+\r
+;=============================================================================\r
+;\r
+; ISendMsg - The OS Interrupt Send primitive.\r
+;  This procedure allows an ISR to send a message to an exchange.\r
+;  This is the same as SendMsg except NO task switch is\r
+;  performed. If a task is waiting at the exchange, the message is\r
+;  associated (linked) with it and it is moved to the RdyQ.\r
+;  It will get a chance to run the next time the RdyQ is evaluated\r
+;  by the Kernel which will probably be by the timer interrupt slicer.\r
+;  Interrupt tasks can use ISendMsg to send single or multiple messages\r
+;  to exchanges during their execution.\r
+;  Interrupts are CLEARED on entry and WILL NOT BE SET on exit!!!\r
+;  It is the responsibility of the caller to set them if desired.\r
+;  ISendMsg is intended only to be used by ISRs in device drivers\r
+;\r
+;\r
+; Procedural Interface :\r
+;\r
+;       ISendMsg(exch, dMsg1, dMsg2):ercType\r
+;\r
+;           exch is a DWORD (4 BYTES) containing the exchange to where the\r
+;           message should be sent.\r
+;\r
+;           dMsg1 and dMsg2 are DWORD messages.\r
+;\r
+; Parameters on stack are the same as _SendMsg.\r
+\r
+PUBLIC __ISendMsg:                                 ;\r
+               CLI                     ;INTS ALWAYS CLEARED AND LEFT THAT WAY!\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV ESI,SendExchange    ; Get Exchange Parameter in ESI\r
+               CMP ESI,nExch           ; If the exchange is out of range\r
+               JNAE ISend00            ; then return to caller with error\r
+               MOV EAX,ercOutOfRange   ; in the EAX register.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+ISend00:\r
+        MOV EAX,ESI             ; Exch => EAX\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX                             ; MAKE ESI <= pExch\r
+               CMP DWORD PTR [EAX+Owner], 0    ; If the exchange is not allocated\r
+               JNE ISend01                             ; return to the caller with error\r
+               MOV EAX,ercNotAlloc     ; in the EAX register.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+ISend01:\r
+               ; Allocate a link block\r
+               MOV EAX,pFreeLB         ; NewLB <= pFreeLB;\r
+               OR EAX,EAX              ; IF pFreeLB=NIL THEN No LBs;\r
+               JNZ SHORT ISend02        ;\r
+               MOV EAX,ercNoMoreLBs    ; caller with error in the EAX register\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+ISend02:\r
+               MOV EBX,[EAX+NextLB]    ; pFreeLB <= pFreeLB^.Next\r
+               MOV pFreeLB,EBX         ;\r
+               DEC _nLBLeft                    ;\r
+\r
+        MOV DWORD PTR [EAX+LBType], DATALB ; This is a Data Link Block\r
+               MOV DWORD PTR [EAX+NextLB],0      ; pLB^.Next <= NIL;\r
+               MOV EBX,MessageLo       ; Get lower half of Msg in EBX\r
+               MOV [EAX+DataLo],EBX    ; Store in lower half of pLB^.Data\r
+               MOV EBX,MessageHi       ; Get upper half of Msg in EBX\r
+               MOV [EAX+DataHi],EBX    ; Store in upper half of pLB^.Data\r
+               PUSH EAX                ; Save pLB on the stack\r
+               CALL deQueueTSS         ; DeQueue a TSS on that Exch\r
+               OR  EAX,EAX             ; Did we get one?\r
+               JNZ ISend03             ; Yes, give up the message\r
+               POP EAX                 ; No, Get the pLB just saved\r
+               CALL enQueueMsg         ; EnQueue the Message on Exch\r
+               JMP ISend04             ; And get out!\r
+ISend03:\r
+        POP EBX                 ; Get the pLB just saved into EBX\r
+               MOV [EAX+pLBRet],EBX    ; and put it in the TSS\r
+               CALL enQueueRdy         ; EnQueue the TSS on the RdyQ\r
+ISend04:\r
+        XOR EAX,EAX             ; Return to Caller with erc ok.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 12                 ;\r
+\r
+;=============================================================================\r
+;\r
+; Wait - The kernel wait primitive. This procedure allows a task to\r
+;  receive information from another task from an exchange.  If no\r
+;  message is at the exchange, the task is placed on the exchange\r
+;  and the ReadyQ is reevaluated to make the next tast run.\r
+;\r
+; A result code is returned in the EAX register.\r
+;\r
+; Procedural Interface :\r
+;\r
+;       Wait(exch,pdqMsgRet):ercType\r
+;\r
+;           exch is a DWORD (4 BYTES) containing the exchange to where the\r
+;           message should be sent.\r
+;\r
+;           pMessage is a pointer to an 8 byte area where the\r
+;           message is stored.\r
+;\r
+WaitExchange   EQU [EBP+10h]\r
+pMessage               EQU [EBP+0Ch]\r
+;\r
+;\r
+PUBLIC __WaitMsg:                              ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV ESI,WaitExchange    ; Get Exchange Parameter in ESI\r
+               CMP ESI,nExch           ; If the exchange is out of range\r
+               JNAE Wait00                     ; the return to caller with error\r
+               MOV EAX,ercOutOfRange   ; in the EAX register.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                                  ;\r
+Wait00:\r
+               MOV EAX,ESI             ; ExchId => EAX\r
+               MOV EBX,sEXCH           ; Compute offset of ExchId in rgExch\r
+               MUL EBX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX             ; Put Exch in to ESI\r
+               CMP DWORD PTR [EAX+Owner], 0   ; If the exchange is not allocated\r
+               JNE Wait01              ; return to the caller with error\r
+               MOV EAX,ercNotAlloc     ; in the EAX register.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+Wait01:\r
+               CLI                     ;\r
+               CALL deQueueMsg         ; EAX <= pLB from pExch (ESI)\r
+               OR  EAX,EAX             ; If no message (pLB = NIL) Then\r
+               JZ Wait02               ; Wait for Message Else\r
+               JMP Wait05              ; Get Message and Return\r
+\r
+Wait02:\r
+               MOV EAX,pRunTSS         ; Get pRunTSS in EAX to Wait\r
+\r
+               ;This next section of code Queues up the TSS pointed to\r
+               ;by EAX on the exchange pointed to by ESI\r
+               ; (i.e., we make the current task "wait")\r
+\r
+               MOV DWORD PTR [EAX+NextTSS], 0  ; pTSSin^.Next <= NIL;\r
+               XCHG ESI,EAX                            ; pExch => EAX, pTSSin => ESI\r
+               CMP DWORD PTR [EAX+EHead], 0    ; if ..TSSHead = NIL\r
+               JNE Wait025                                             ; then\r
+               MOV [EAX+EHead],ESI     ;  ..TSSHead <= pTSSin;\r
+               MOV [EAX+ETail],ESI     ;  ..TSSTail <= pTSSin;\r
+               MOV DWORD PTR [EAX+fEMsg], 0            ; Flag it as a TSS (vice a Msg)\r
+               XCHG ESI,EAX            ; Make ESI <= pExch Again\r
+               JMP SHORT Wait03                  ; else\r
+Wait025:\r
+           MOV EDX,[EAX+ETail]     ;  ..TSSTail^.NextTSS <= pTSSin;\r
+               MOV [EDX+NextTSS],ESI   ;\r
+               MOV [EAX+ETail],ESI     ;  ..TSSTail <= pTSSin;\r
+               MOV DWORD PTR [EAX+fEMsg], 0            ; Flag it as a TSS (vice a Msg)\r
+               XCHG ESI,EAX            ; Make ESI <= pExch Again\r
+\r
+               ;We just placed the current TSS on an exchange,\r
+               ;now we get the next TSS to run (if there is one)\r
+\r
+Wait03:\r
+               CALL deQueueRdy         ; Get highest priority TSS off the RdyQ\r
+               OR EAX, EAX                             ; Anyone ready to run?\r
+               JNZ Wait035             ; Yes (jump to check pTSS)\r
+\r
+               MOV EDI, 1\r
+               MOV dfHalted, EDI\r
+               INC _nHalts\r
+               STI                     ; No, then HLT CPU until ready\r
+               HLT                     ; Halt CPU and wait for interrupt\r
+               CLI                     ; An interrupt has occured. Clear Interrupts\r
+               XOR EDI,EDI\r
+               MOV dfHalted, EDI\r
+               JMP Wait03              ; Check for a task to switch to\r
+\r
+Wait035:\r
+               CMP EAX,pRunTSS         ; Same one as before???\r
+               JE Wait04               ; You bet! NO SWITCH!!!\r
+\r
+               ;Now we switch tasks by placing the address of the\r
+               ;new TSS in pRunTSS and jumping to it.  This forces\r
+               ;a 386 task switch.\r
+\r
+               MOV pRunTSS,EAX                 ; Make high priority TSS Run.\r
+               MOV BX,[EAX+Tid]                ;\r
+               MOV TSS_Sel,BX                  ;\r
+               INC _nSwitches\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]             ; JUMP TSS (Switch Tasks)\r
+\r
+               ; A task has just finished "Waiting"\r
+               ; We are now in the new task with its memory space\r
+               ; (or the same task if he was high pri & had a msg)\r
+               ; If this is a system service it may need RqBlk address aliases\r
+               ; If it is an OS service we alias in OS memory!\r
+\r
+Wait04:\r
+               MOV EDX,pRunTSS         ; Put the TSS in EAX into EDX\r
+               MOV EAX,[EDX+pLBRet]    ; Get the pLB in EAX\r
+Wait05:\r
+               ; if we got here, we have either switched tasks\r
+               ; and we are delivering a message (or Req) to the new task,\r
+               ; or the there was a message waiting at the exch of\r
+               ; the first caller and we are delivering it.\r
+               ; Either way, the message is already deQueued from\r
+               ; the exch and the critical part of WaitMsg is over.\r
+               ; We can start interrupts again except when we have\r
+               ; to return the Link Block to the pool (free it up)\r
+\r
+               STI                                                                     ; WE CAN RESTART INTERRUPTS HERE\r
+               CMP DWORD PTR [EAX+LBType],REQLB        ; Is the link block a Req Link Block?\r
+               JNE Wait06                                                      ; No, Treat it as a data link block\r
+\r
+               ;pLB.DataLo is RqHandle (pRqBlk)\r
+\r
+               PUSH EAX                                ; Save ptr to Link Block\r
+               MOV EBX,[EAX+DataLo]    ; Get pRqBlk into EBX\r
+\r
+               ;Now we set up to alias the memory for the service\r
+               ; (Alias the 2 Pointers in the RqBlk)\r
+               ;_AliasMem(pMem, dcbMem, dJobNum, ppAliasRet): dError\r
+\r
+               MOV ECX, [EBX+cbData1]          ;\r
+               OR ECX, ECX                                     ;is cbData1 0?\r
+               JZ Wait051                                      ;Yes\r
+\r
+               MOV EAX, [EBX+pData1]           ;\r
+               OR EAX, EAX                                     ;is pData1 NULL?\r
+               JZ Wait051                                      ;Yes\r
+\r
+                                                                       ;Set up params for AliasMem\r
+               PUSH EAX                                        ;pMem\r
+               PUSH ECX                                        ;cbMem\r
+               MOV EAX, [EBX+RqOwnerJob]\r
+               PUSH EAX                                        ;dJobNum\r
+               ADD EBX, pData1                         ;Offset to pData1 in RqBlk\r
+               PUSH EBX                                        ;Linear Address of pData1\r
+               CALL FWORD PTR _AliasMem\r
+               OR EAX, EAX                                     ;Error??\r
+               JZ Wait051                                      ;No, continue\r
+               POP EBX                                         ;Make stack right\r
+               MOV ESP,EBP                     ;Return Error...\r
+               POP EBP                         ;\r
+               RETF 8                          ;\r
+\r
+Wait051:                                                       ;Second Pointer (pData2)\r
+               POP EAX                                         ;Restore ptr to Link Block\r
+               PUSH EAX                                        ;Save again\r
+               MOV EBX,[EAX+DataLo]            ; Get pRqBlk into EBX\r
+\r
+               MOV ECX, [EBX+cbData2]          ;\r
+               OR ECX, ECX                                     ;is cbData2 0?\r
+               JZ Wait052                                      ;Yes\r
+\r
+               MOV EAX, [EBX+pData2]           ;\r
+               OR EAX, EAX                                     ;is pData2 NULL?\r
+               JZ Wait052                                      ;Yes\r
+                                                                       ;Set up params for AliasMem\r
+               PUSH EAX                                        ;pMem\r
+               PUSH ECX                                        ;cbMem\r
+               MOV EAX, [EBX+RqOwnerJob]\r
+               PUSH EAX                                        ;dJobNum\r
+               ADD EBX, pData2                         ;Offset to pData2 in RqBlk\r
+               PUSH EBX                                        ;Linear Address of PData1\r
+               CALL FWORD PTR _AliasMem\r
+               OR EAX, EAX                                     ;Error??\r
+               JZ Wait052                                      ;No, continue\r
+               POP EBX                                         ;Make stack right\r
+               MOV ESP,EBP                     ;Return Error...\r
+               POP EBP                         ;\r
+               RETF 8                          ;\r
+\r
+Wait052:\r
+               POP EAX                                 ;Restore ptr to Link Block\r
+Wait06:\r
+               MOV EBX,[EAX+DataLo]    ; Get pLB^.Data into ECX:EBX\r
+               MOV ECX,[EAX+DataHi]    ;\r
+               MOV EDX,pMessage                ; Get Storage Addr in EDX\r
+               MOV [EDX],EBX                   ; Put pLB^.Data in specified\r
+               MOV [EDX+4],ECX             ; memory space (EDX)\r
+\r
+               ;Return the LB to the pool\r
+               CLI\r
+               MOV EBX,pFreeLB         ; pLBin^.Next <= pFreeLB;\r
+               MOV [EAX+NextLB],EBX    ;\r
+               MOV pFreeLB,EAX         ; pFreeLB <= pLBin;\r
+               INC _nLBLeft                    ;\r
+               STI                     ;\r
+               XOR EAX,EAX             ; ErcOK! (0)\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+\r
+;\r
+;=============================================================================\r
+;\r
+; CheckMsg - The kernel Check primitive. This procedure provides access\r
+; to the operating system by allowing a task to receive information\r
+; from another process WITHOUT BLOCKING. In other words, if no message is\r
+; available Check returns to the caller. If a message IS available it\r
+; is returned to the caller immediately. The caller is never placed on\r
+; an exchange and the RdyQ is not evaluated.\r
+;\r
+; A result code is returned in the EAX register.\r
+;\r
+; Procedureal Interface :\r
+;\r
+;       CheckMsg(exch,pdqMsg):ercType\r
+;\r
+;           exch is a DWORD (4 BYTES) containing the exchange to where the\r
+;           message should be sent.\r
+;\r
+;           pdqMsg is a pointer to an 8 byte area where the message is stored.\r
+;\r
+ChkExchange   EQU [EBP+10h]\r
+pCkMessage    EQU [EBP+0Ch]\r
+\r
+PUBLIC __CheckMsg:               ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV ESI,ChkExchange     ; Get Exchange Parameter in ESI\r
+               CMP ESI,nExch           ; If the exchange is out of range\r
+               JNAE Chk01              ; the return to caller with error\r
+               MOV EAX,ercOutOfRange   ; in the EAX register.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                  ;\r
+Chk01:\r
+               MOV EAX,ESI             ; Exch => EAX\r
+               MOV EBX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EBX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ESI,EAX                             ; Put pExch in to ESI\r
+               CMP DWORD PTR [EAX+Owner], 0    ; If the exchange is not allocated\r
+               JNE Chk02                               ; return to the caller with error\r
+\r
+               MOV EAX,ercNotAlloc     ; in the EAX register.\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                                  ;\r
+Chk02:\r
+               CLI                     ; Can't be interrupted\r
+               CALL deQueueMsg         ; EAX <= pLB from pExch (ESI)\r
+               OR  EAX,EAX             ; If pLB = NIL Then\r
+               JNZ Chk03               ; Go to get msg and return\r
+\r
+               STI                     ;\r
+               MOV EAX,ercNoMsg        ; return with erc no msg\r
+               MOV ESP,EBP             ;\r
+               POP EBP                                 ;\r
+               RETF 8                                  ;\r
+Chk03:\r
+               STI                                             ;We can be interrupted again\r
+\r
+               CMP DWORD PTR [EAX+LBType],REQLB        ; Is the link block a Req Link Block?\r
+               JNE Chk04                               ; No, Treat it as a data link block\r
+\r
+               ;pLB.DataLo is RqHandle (pRqBlk)\r
+\r
+               PUSH EAX                                ; Save ptr to Link Block\r
+               MOV EBX,[EAX+DataLo]    ; Get pRqBlk into EBX\r
+\r
+               ;Now we set up to alias the memory for the service\r
+               ; (Alias the 2 Pointers in the RqBlk)\r
+               ;_AliasMem(pMem, dcbMem, dJobNum, ppAliasRet): dError\r
+\r
+               MOV ECX, [EBX+cbData1]          ;\r
+               OR ECX, ECX                                     ;is cbData1 0?\r
+               JZ Chk031                                       ;Yes\r
+\r
+               MOV EAX, [EBX+pData1]           ;\r
+               OR EAX, EAX                                     ;is pData1 NULL?\r
+               JZ Chk031                                       ;Yes\r
+                                                                       ;Set up params for AliasMem\r
+               PUSH EAX                                        ;pMem\r
+               PUSH ECX                                        ;cbMem\r
+               MOV EAX, [EBX+RqOwnerJob]\r
+               PUSH EAX                                        ;dJobNum\r
+               ADD EBX, pData1                         ;Offset to pData1 in RqBlk\r
+               PUSH EBX                                        ;Linear Address of pData1\r
+               CALL FWORD PTR _AliasMem\r
+               OR EAX, EAX                                     ;Error??\r
+               JZ Chk031                                       ;No, continue\r
+               POP EBX                                         ;Make stack right\r
+               MOV ESP,EBP                     ;Return Error...\r
+               POP EBP                         ;\r
+               RETF 8                          ;\r
+\r
+Chk031:                                                                ;Second Pointer (pData2)\r
+               POP EAX                                         ;Restore ptr to Link Block\r
+               PUSH EAX                                        ;Save again\r
+               MOV EBX,[EAX+DataLo]            ; Get pRqBlk into EBX\r
+\r
+               MOV ECX, [EBX+cbData2]          ;\r
+               OR ECX, ECX                                     ;is cbData2 0?\r
+               JZ Chk032                                       ;Yes\r
+\r
+               MOV EAX, [EBX+pData2]           ;\r
+               OR EAX, EAX                                     ;is pData2 NULL?\r
+               JZ Chk032                                       ;Yes\r
+                                                                       ;Set up params for AliasMem\r
+               PUSH EAX                                        ;pMem\r
+               PUSH ECX                                        ;cbMem\r
+               MOV EAX, [EBX+RqOwnerJob]\r
+               PUSH EAX                                        ;dJobNum\r
+               ADD EBX, pData2                         ;Offset to pData2 in RqBlk\r
+               PUSH EBX                                        ;Linear Address of PData1\r
+               CALL FWORD PTR _AliasMem\r
+               OR EAX, EAX                                     ;Error??\r
+               JZ Chk032                                       ;No, continue\r
+               POP EBX                                         ;Make stack right\r
+               MOV ESP,EBP                     ;Return Error...\r
+               POP EBP                         ;\r
+               RETF 8                          ;\r
+\r
+Chk032:\r
+               POP EAX                                         ;Restore Ptr to Link Block\r
+\r
+Chk04:\r
+        MOV EBX,[EAX+DataLo]    ; Get pLB^.Data into ECX:EBX\r
+               MOV ECX,[EAX+DataHi]    ;\r
+               MOV EDX,pCkMessage              ; Get Storage Addr in EDX\r
+               MOV [EDX],EBX           ; Put pLB^.Data in specified\r
+               MOV [EDX+4],ECX                 ; memory space (EDX)\r
+\r
+               ;Return the LB to the pool\r
+               CLI\r
+               MOV EBX,pFreeLB         ; pLBin^.Next <= pFreeLB;\r
+               MOV [EAX+NextLB],EBX    ;\r
+               MOV pFreeLB,EAX         ; pFreeLB <= pLBin;\r
+               INC _nLBLeft                    ;\r
+               STI                     ;\r
+\r
+               XOR EAX,EAX             ;ErcOK! (0)\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 8                                  ;\r
+\r
+;====================================================================\r
+; NewTask --- OS PUBLIC - Creates a new task and schedules it for execution.\r
+; used primarily to create a task for a job other than the one you are in.\r
+; The OS uses this to create the initial task for a newly loaded job.\r
+;\r
+; The OS stacks are preallocated as part of the TSS.  If this is an OS\r
+; task (CodeSeg = 8) then we load the TSS Stack into TSS_ESP and\r
+; TSS_ESP0, otherwise we take the ESP param and place it into TSS_ESP.\r
+;\r
+;  Procedural interface:\r
+;\r
+;      NewTask(JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP): dErcRet\r
+;\r
+;\r
+NTS_Job                EQU [EBP+36]            ;Job Num for this task\r
+NTS_CS         EQU [EBP+32]            ;8 for OS, 18h for user task\r
+NTS_Pri                EQU [EBP+28]            ;Priority of this task\r
+NTS_fDbg       EQU [EBP+24]            ;TRUE for DEBUGing\r
+NTS_Exch       EQU [EBP+20]            ;Exchange for TSS\r
+NTS_ESP                EQU [EBP+16]            ;Initial stack pointer\r
+NTS_EIP                EQU [EBP+12]            ;Task start address\r
+\r
+PUBLIC __NewTask:                    ;\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+\r
+        MOV EDX, NTS_Pri                       ;\r
+               CMP EDX, nPRI-1                         ;Priority OK?\r
+               JBE NT0000\r
+               MOV EAX,ercBadPriority\r
+               JMP NTEnd\r
+NT0000:\r
+               MOV ECX, NTS_Exch\r
+        CMP ECX, nExch                                 ;Exch in range?\r
+               JBE NT0001\r
+               MOV EAX,ercOutOfRange\r
+               JMP NTEnd\r
+NT0001:\r
+               CLI                     ;we can't be interrupted\r
+               MOV EAX,pFreeTSS        ; NewTSS <= pFreeTSS;\r
+               OR EAX,EAX              ; IF pFreeTSS=NIL THEN Return;\r
+               JNZ NT0002              ;\r
+               MOV EAX,ercNoMoreTSSs       ;No...\r
+               JMP NTEnd\r
+NT0002:\r
+               MOV EBX,[EAX+NextTSS]   ; pFreeTSS <= pFreeTSS^.Next\r
+               MOV pFreeTSS,EBX        ;\r
+               DEC _nTSSLeft                   ;\r
+               STI\r
+\r
+               ;EAX now has pNewTSS\r
+\r
+               MOV [EAX+Priority],DL       ;put Priority into TSS\r
+               MOV DWORD PTR [EAX+TSS_EFlags],0202h  ;Load the Flags Register\r
+               MOV [EAX+TSS_Exch], ECX         ;Put new Exch in TSS (ECX is free)\r
+               MOV EBX, NTS_EIP                        ;mov EIP into TSS (Start Address)\r
+               MOV [EAX+TSS_EIP],EBX\r
+               MOV EBX, NTS_ESP                        ;mov ESP into TSS\r
+               MOV [EAX+TSS_ESP],EBX\r
+               MOV [EAX+TSS_ESP0],EBX          ;\r
+               MOV ECX, NTS_CS                         ;mov CS into TSS\r
+               MOV [EAX+TSS_CS],CX\r
+\r
+               PUSH EAX                                        ;Save pNewTSS\r
+\r
+               ;Now we get pJCB from JobNum they passed in so we can\r
+               ;get the PD from the JCB\r
+\r
+               MOV EAX, NTS_Job                        ;Set up to call GetpJCB\r
+               CALL GetpJCB                            ;EAX now has pJCB\r
+               MOV ECX, EAX                            ;ECX now has pJCB\r
+\r
+               POP EAX                                         ;Restore pNewTSS to EAX\r
+\r
+               MOV [EAX+TSS_pJCB],ECX          ;Put pJCB into TSS\r
+               MOV EBX, [ECX+JcbPD]        ;Set up to call LinToPhy\r
+\r
+               PUSH EAX                                        ;Save pNewTSS again\r
+\r
+               MOV EAX, NTS_Job                        ;\r
+               CALL LinToPhy                           ;Get Physical Address for PD into EAX\r
+               MOV EBX, EAX\r
+               POP EAX                                         ;pNewTSS into EAX\r
+               MOV [EAX+TSS_CR3],EBX           ;Put Physical Add for PD into TSS_CR3\r
+               CMP DWORD PTR NTS_fDbg, 0       ;Debug on entry?\r
+               JE NT0004                   ;No\r
+               MOV WORD PTR [EAX+TSS_TrapBit], 1    ;Yes\r
+NT0004:\r
+\r
+               MOV EBX, NTS_Pri                        ;Get priority of new task\r
+\r
+               CLI                         ;We can't be interrupted\r
+               MOV EDX,pRunTSS                     ;Get who's running\r
+               CMP BYTE PTR [EDX+Priority],BL  ;Who got the highest Pri?\r
+               JA NT0005                       ;New guy does (lowest num)\r
+               CALL enQueueRdy             ;Just put new guy on the ReadyQue (EAX)\r
+               XOR EAX,EAX                 ;ercOk\r
+               JMP NTEnd                   ;Return to caller\r
+NT0005:\r
+        XCHG EAX,EDX                ;CrntTSS -> EAX, New TSS -> EDX\r
+        PUSH EDX                                       ;Save New TSS\r
+               CALL enQueueRdy             ;\r
+               POP EAX                                         ;New TSS -> EAX\r
+               MOV pRunTSS,EAX             ;Move new TSS into pRunTSS\r
+               MOV BX,[EAX+Tid]            ;Put Selector/Offset in "TSS"\r
+               MOV TSS_Sel,BX              ;\r
+               INC _nSwitches\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]         ;Jump to new TSS\r
+               XOR EAX,EAX                 ;ErcOk\r
+NTEnd:\r
+               STI                         ;\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 28                                         ;\r
+\r
+;====================================================================\r
+; SpawnTask --- OS PUBLIC - Creates a new task in the current job\r
+; and schedules it for execution\r
+;\r
+; Procedural Interface:\r
+; SpawnTask(pEntry,    dPriority, fDebug, pStack, fOSCode);\r
+;\r
+;\r
+pEntryST       EQU DWORD PTR [EBP+28]\r
+dPriST         EQU DWORD PTR [EBP+24]\r
+fDebugST       EQU DWORD PTR [EBP+20]\r
+pStackST       EQU DWORD PTR [EBP+16]\r
+fOSCodeST      EQU DWORD PTR [EBP+12]\r
+\r
+NewExchST      EQU DWORD PTR [EBP-4]\r
+NewTSSST       EQU DWORD PTR [EBP-8]\r
+\r
+PUBLIC __SpawnTask:                  ;\r
+               PUSH EBP                    ;\r
+               MOV EBP,ESP                 ;\r
+               SUB ESP, 8                                      ;two local DWORD vars\r
+               CMP dPriST, nPRI-1                      ;Priority OK?\r
+               JBE ST0001\r
+               MOV EAX,ercBadPriority\r
+               JMP STEnd\r
+ST0001:\r
+               LEA EAX, NewExchST                      ;Allocate exchange\r
+               PUSH EAX\r
+               CALL FWORD PTR _AllocExch\r
+               OR EAX, EAX                                     ;see if we got an error\r
+               JNZ STEnd                                       ;Yup, bad news\r
+\r
+               ;Allocate a new TSS\r
+               CLI                     ;we can't be interrupted\r
+               MOV EAX,pFreeTSS        ; NewTSS <= pFreeTSS;\r
+               OR EAX,EAX              ; IF pFreeTSS=NIL THEN Return;\r
+               JNZ ST0002              ;\r
+               STI\r
+\r
+               ;Dealloc Exch if we didn't get a TSS\r
+               PUSH NewExchST\r
+               CALL FWORD PTR _DeAllocExch\r
+               MOV EAX,ercNoMoreTSSs       ;No...\r
+               JMP NTEnd\r
+ST0002:\r
+               MOV EBX,[EAX+NextTSS]   ; pFreeTSS <= pFreeTSS^.Next\r
+               MOV pFreeTSS,EBX        ;\r
+               DEC _nTSSLeft                   ;\r
+               STI\r
+\r
+               MOV NewTSSST, EAX                       ;Save new TSS\r
+        MOV EBX, NewExchST                     ;mov exch into TSS\r
+               MOV [EAX+TSS_Exch],EBX\r
+               MOV WORD PTR [EAX+TSS_CS], OSCodeSel    ;Defaults to OS code selector\r
+               CMP fOSCodeST, 0\r
+               JNE ST0003\r
+               MOV WORD PTR [EAX+TSS_CS], JobCodeSel   ;Make OS code selector\r
+ST0003:\r
+               MOV EBX,pEntryST                        ;mov EIP into TSS\r
+               MOV [EAX+TSS_EIP],EBX\r
+               MOV EBX, pStackST                       ;mov ESP into TSS\r
+               MOV [EAX+TSS_ESP],EBX\r
+               MOV [EAX+TSS_ESP0],EBX\r
+               MOV EBX, pRunTSS\r
+               MOV EDX, [EBX+TSS_pJCB]         ;Get pJCB from Crnt Task\r
+               MOV [EAX+TSS_pJCB],EDX\r
+               MOV EDX, [EBX+TSS_CR3]          ;Get CR3 from crnt task\r
+               MOV [EAX+TSS_CR3],EDX           ; move into new TSS\r
+               MOV DWORD PTR [EAX+TSS_EFlags],0202h  ;Load the Flags Register\r
+               CMP fDebugST, 0                                         ;Debug on entry?\r
+               JE ST0004                               ;No\r
+               MOV WORD PTR [EAX+TSS_TrapBit], 1   ;Yes\r
+ST0004:\r
+        MOV EBX, dPriST                                ;mov priority into BL\r
+               MOV [EAX+Priority],BL       ;put in TSS\r
+\r
+               CLI                         ;we can't be interrupted\r
+               MOV EDX,pRunTSS             ;Get who's running\r
+               CMP [EDX+Priority],BL       ;Who got the highest Pri?\r
+               JA ST0005                   ;If crnt >, New guy does (lowest num)\r
+               CALL enQueueRdy             ;Old guy does, just put new guy on Q.\r
+               XOR EAX,EAX                 ;ercOk\r
+               JMP STEnd                   ;Return to caller\r
+ST0005:\r
+        XCHG EAX,EDX                ;CrntTSS -> EAX, New TSS -> EDX\r
+        PUSH EDX                                       ;New TSS -> Stack\r
+               CALL enQueueRdy             ;Place crnt TSS on Q\r
+               POP EAX                                         ;New TSS -> EAX\r
+               MOV pRunTSS,EAX             ;Move new TSS into pRunTSS\r
+               MOV BX,[EAX+Tid]            ;Put Selector/Offset in "TSS"\r
+               MOV TSS_Sel,BX              ;\r
+               INC _nSwitches\r
+               MOV EAX, TimerTick              ;Save time of this switch for scheduler\r
+               MOV SwitchTick, EAX             ;\r
+               JMP FWORD PTR [TSS]         ;Jump to new TSS\r
+               XOR EAX,EAX                 ;ErcOk\r
+STEnd:\r
+               STI                         ;\r
+               MOV ESP,EBP                 ;\r
+               POP EBP                     ;\r
+               RETF 20                                         ;\r
+\r
+;=============================================================================\r
+;\r
+; AllocExch - The kernel Allocate Exchange primitive. This procedure\r
+; provides access to the operating system by allowing a TASK to\r
+; allocate a message port for the transmission and reception of messages from\r
+; another process.\r
+;\r
+; Procedural Interface :\r
+;\r
+;       AllocExch(pExchRet):dError\r
+;\r
+;          pExchRet is a pointer to where you want the Exchange Handle\r
+;          returned.  The Exchange Handle is a DWORD (4 BYTES).\r
+;\r
+;=============================================================================\r
+\r
+PUBLIC __AllocExch:              ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               XOR ESI,ESI             ; Zero the Exch Index\r
+               MOV EBX,prgExch         ; EBX <= ADR rgExch\r
+               MOV ECX,nExch           ; Get number of exchanges in ECX\r
+AE000:\r
+               CLI                                     ;\r
+               CMP DWORD PTR [EBX+Owner], 0    ; Is this exchange free to use\r
+               JE AE001                                ; If we found a Free Exch, JUMP\r
+               ADD EBX,sEXCH           ; Point to the next Exchange\r
+               INC ESI                 ; Increment the Exchange Index\r
+               LOOP AE000              ; Keep looping until we are done\r
+               STI                     ;\r
+               MOV EAX,ercNoMoreExch   ; There are no instances of the Exch\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+AE001:\r
+               MOV EDX,[EBP+0CH]       ; Get the pExchRet in EDX\r
+               MOV [EDX],ESI                   ; Put Index of Exch at pExchRet\r
+               MOV EDX,pRunTSS         ; Get pRunTSS in EDX\r
+               MOV EAX,[EDX+TSS_pJCB]  ; Get the pJCB in EAX\r
+               MOV [EBX+Owner],EAX     ; Make the Exch owner the Job\r
+               STI                     ;\r
+               MOV DWORD PTR [EBX+EHead],0     ; Make the msg/TSS queue NIL\r
+               MOV DWORD PTR [EBX+ETail],0     ;\r
+               DEC _nEXCHLeft                                  ; Stats\r
+               XOR EAX,EAX             ;ercOK (0)\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+;=============================================================================\r
+;\r
+; DeAllocExch - The kernel DeAllocate Exchange primitive. It allows a TASK\r
+; to deallocate a "message port."  It also deQueues any messages, and frees\r
+; up any Link Blocks, TSSs, and RQBs, attached to the exchange\r
+;\r
+; Procedural Interface :\r
+;\r
+;       DeAllocExch(Exch):ercType\r
+;\r
+;           Exch is the Exchange Handle the process is asking to be released.\r
+\r
+PUBLIC __DeAllocExch:                 ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+\r
+               MOV ESI,[EBP+0CH]       ; Load the Exchange Index in ESI\r
+               MOV EAX,ESI             ; Get the Exchange Index in EAX\r
+               MOV EDX,sEXCH           ; Compute offset of Exch in rgExch\r
+               MUL EDX                 ;\r
+               MOV EDX,prgExch         ; Add offset of rgExch => EAX\r
+               ADD EAX,EDX             ;\r
+               MOV ECX,EAX             ; Make a copy in ECX (ECX = pExch)\r
+\r
+               MOV EDX,pRunTSS         ; Get the pRunTSS in EDX\r
+               MOV EBX,[EDX+TSS_pJCB]  ; Get pJCB in EBX\r
+               MOV EDX,[EAX+Owner]     ; Get the Exchange Owner in EDX\r
+               CMP EBX,EDX             ; If the CurrProc owns the Exchange,\r
+               JE DE000                ; yes\r
+               CMP EBX, OFFSET MonJCB  ; if not owner, is this the OS???\r
+               JE DE000                ; yes\r
+               MOV EAX,ercNotOwner     ;\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+DE000:\r
+               CLI                     ;\r
+               CMP DWORD PTR [ECX+fEMsg],0 ; See if a message may be queued\r
+               JE DE001                        ; No. Go check for Task (TSS)\r
+               MOV ESI, ECX                            ; ESI must point to Exch for deQueue\r
+               CALL deQueueMsg         ; Yes, Get the message off of the Exchange\r
+               OR EAX, EAX\r
+               JZ DE002                                ; Nothing there. Go free the Exch.\r
+\r
+               ;Return the LB to the pool\r
+               MOV EBX,pFreeLB         ; pLBin^.Next <= pFreeLB;\r
+               MOV [EAX+NextLB],EBX    ;\r
+               MOV pFreeLB,EAX         ; pFreeLB <= pLBin;\r
+               INC _nLBLeft                    ;\r
+               JMP DE000               ; Go And Check for more.\r
+\r
+               ; If we find an RqBlk on the exchange we must respond\r
+               ;with ErcInvalidExch before we continue! This will\r
+               ;only happen if a system service writer doesn't follow\r
+               ;instructions or a service crashes!\r
+               ;\r
+DE001:\r
+               CMP DWORD PTR [ECX+EHead], 0    ; Check to See if TSS is queued\r
+               JE DE002                ; NIL = Empty, JUMP\r
+               MOV ESI, ECX                    ; ESI must point to Exch for deQueue\r
+               CALL deQueueTSS         ; Get the TSS off of the Exchange\r
+\r
+               ;Free up the TSS (add it to the free list)\r
+               MOV EBX,pFreeTSS        ; pTSSin^.Next <= pFreeTSS;\r
+               MOV [EAX+NextTSS],EBX                   ;\r
+               MOV DWORD PTR [EAX+TSS_pJCB], 0 ; Make TSS invalid\r
+               MOV pFreeTSS,EAX                        ; pFreeTSS <= pTSSin;\r
+               INC _nTSSLeft                   ;\r
+\r
+               JMP DE001               ; Go And Check for more.\r
+DE002:\r
+               MOV DWORD PTR [ECX+Owner], 0     ; Free up the exchange.\r
+               MOV DWORD PTR [ECX+fEMsg], 0     ; Reset msg Flag.\r
+               INC _nEXCHLeft                  ; Stats\r
+               STI                     ;\r
+               XOR EAX,EAX             ;ercOK (0)\r
+               MOV ESP,EBP             ;\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+;=============================================================================\r
+;\r
+; GetTSSExch - This returns the Exchange of the current TSS to the\r
+; caller.  This is primarily provided for System Services that provide\r
+; direct access blocking calls for customers.\r
+;\r
+; Procedural Interface :\r
+;\r
+;       GetTSSExch(pExchRet):dError\r
+;\r
+;          pExchRet is a pointer to where you want the Exchange Handle\r
+;          returned.  The Exchange is a DWORD (4 BYTES).\r
+;\r
+PUBLIC __GetTSSExch:            ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV EAX,pRunTSS                 ; Get the Ptr To the Running TSS\r
+               MOV ESI,[EBP+0CH]       ; Get the pExchRet in EDX\r
+               MOV EBX, [EAX+TSS_Exch] ; Get Exch in EBX\r
+               MOV [ESI],EBX                   ; Put Index of Exch at pExchRet\r
+               XOR EAX, EAX            ; ErcOK\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+;=============================================================================\r
+;\r
+; SetPriority - This sets the priority of the task that called it\r
+; to the priority specified in the single parameter.\r
+;\r
+; Procedural Interface :\r
+;\r
+;       SetPriority(bPriority):dError\r
+;\r
+;          bPriority is a byte with the new priority.\r
+;\r
+PUBLIC __SetPriority            ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV EAX,pRunTSS                 ; Get the Ptr To the Running TSS\r
+               MOV EBX,[EBP+0CH]       ; Get the new pri into EBX\r
+               AND EBX, 01Fh                   ; Nothing higher than 31!\r
+               MOV BYTE PTR [EAX+Priority], BL ;Put it in the TSS\r
+               XOR EAX, EAX            ; ErcOK - No error.\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+;=============================================================================\r
+;\r
+; GetPriority - This gets the priority of the task that called it\r
+; and passes it to bPriorityRet.\r
+;\r
+; Procedural Interface :\r
+;\r
+;       SetPriority(bPriorityRet):dError\r
+;\r
+;          bPriorityret is a pointer to a byte where you want the\r
+;             priority returned.\r
+;\r
+PUBLIC __GetPriority            ;\r
+               PUSH EBP                ;\r
+               MOV EBP,ESP             ;\r
+               MOV EAX,pRunTSS                 ; Get the Ptr To the Running TSS\r
+               MOV EBX,[EBP+0CH]       ; Get the return pointer into EBX\r
+               MOV DL, BYTE PTR [EAX+Priority]\r
+               MOV BYTE PTR [EBX], DL  ;\r
+               XOR EAX, EAX            ; ErcOK - No error.\r
+               POP EBP                 ;\r
+               RETF 4                  ;\r
+\r
+;======================== End of Module ======================\r